Code:
/ 4.0 / 4.0 / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / fx / src / WinForms / Managed / System / WinForms / TreeView.cs / 1305376 / TreeView.cs
//------------------------------------------------------------------------------ //// Copyright (c) Microsoft Corporation. All rights reserved. // //----------------------------------------------------------------------------- /* */ namespace System.Windows.Forms { using System.Runtime.Remoting; using System.Runtime.InteropServices; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System; using System.Security.Permissions; using System.Security; using System.Drawing; using System.Windows.Forms.Internal; using System.Drawing.Design; using System.ComponentModel; using System.ComponentModel.Design; using System.Collections; using Microsoft.Win32; using System.Reflection; using System.Windows.Forms.Layout; using System.Globalization; using System.Windows.Forms.VisualStyles; ////// /// [ ComVisible(true), ClassInterface(ClassInterfaceType.AutoDispatch), DefaultProperty("Nodes"), DefaultEvent("AfterSelect"), Docking(DockingBehavior.Ask), Designer("System.Windows.Forms.Design.TreeViewDesigner, " + AssemblyRef.SystemDesign), SRDescription(SR.DescriptionTreeView) ] public class TreeView : Control { private static readonly int MaxIndent = 32000; // Maximum allowable TreeView indent private static readonly string backSlash = "\\"; private const int DefaultTreeViewIndent = 19; private DrawTreeNodeEventHandler onDrawNode; private NodeLabelEditEventHandler onBeforeLabelEdit; private NodeLabelEditEventHandler onAfterLabelEdit; private TreeViewCancelEventHandler onBeforeCheck; private TreeViewEventHandler onAfterCheck; private TreeViewCancelEventHandler onBeforeCollapse; private TreeViewEventHandler onAfterCollapse; private TreeViewCancelEventHandler onBeforeExpand; private TreeViewEventHandler onAfterExpand; private TreeViewCancelEventHandler onBeforeSelect; private TreeViewEventHandler onAfterSelect; private ItemDragEventHandler onItemDrag; private TreeNodeMouseHoverEventHandler onNodeMouseHover; private EventHandler onRightToLeftLayoutChanged; internal TreeNode selectedNode = null; private ImageList.Indexer imageIndexer; private ImageList.Indexer selectedImageIndexer; private bool setOddHeight = false; private TreeNode prevHoveredNode = null; private bool hoveredAlready = false; private bool rightToLeftLayout = false; [SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources")] private IntPtr hNodeMouseDown = IntPtr.Zero;//ensures we fire nodeclick on the correct node private const int TREEVIEWSTATE_hideSelection = 0x00000001; private const int TREEVIEWSTATE_labelEdit = 0x00000002; private const int TREEVIEWSTATE_scrollable = 0x00000004; private const int TREEVIEWSTATE_checkBoxes = 0x00000008; private const int TREEVIEWSTATE_showLines = 0x00000010; private const int TREEVIEWSTATE_showPlusMinus = 0x00000020; private const int TREEVIEWSTATE_showRootLines = 0x00000040; private const int TREEVIEWSTATE_sorted = 0x00000080; private const int TREEVIEWSTATE_hotTracking = 0x00000100; private const int TREEVIEWSTATE_fullRowSelect = 0x00000200; private const int TREEVIEWSTATE_showNodeToolTips = 0x00000400; private const int TREEVIEWSTATE_doubleclickFired = 0x00000800; private const int TREEVIEWSTATE_mouseUpFired = 0x00001000; private const int TREEVIEWSTATE_showTreeViewContextMenu = 0x00002000; private const int TREEVIEWSTATE_lastControlValidated = 0x00004000; private const int TREEVIEWSTATE_stopResizeWindowMsgs = 0x00008000;//VSWhidbey 466949 private const int TREEVIEWSTATE_ignoreSelects = 0x00010000;//VSWhidbey 384294, 490763. // PERF: take all the bools and put them into a state variable private System.Collections.Specialized.BitVector32 treeViewState; // see TREEVIEWSTATE_ consts above ////// Displays a hierarchical list of items, or nodes. Each /// node includes a caption and an optional bitmap. The user can select a node. If /// it has sub-nodes, the user can collapse or expand the node. /// /// ////// internal ImageList.Indexer ImageIndexer { get { if (imageIndexer == null) { imageIndexer = new ImageList.Indexer(); } imageIndexer.ImageList = ImageList; return imageIndexer; } } /// /// internal ImageList.Indexer SelectedImageIndexer { get { if (selectedImageIndexer == null) { selectedImageIndexer = new ImageList.Indexer(); } selectedImageIndexer.ImageList = ImageList; return selectedImageIndexer; } } private ImageList imageList; private int indent = -1; private int itemHeight = -1; private string pathSeparator = backSlash; private BorderStyle borderStyle = System.Windows.Forms.BorderStyle.Fixed3D; internal TreeNodeCollection nodes = null; internal TreeNode editNode; internal TreeNode root; internal Hashtable nodeTable = new Hashtable(); internal bool nodesCollectionClear = false; //this is set when the treeNodeCollection is getting cleared and used by TreeView private MouseButtons downButton; private TreeViewDrawMode drawMode = TreeViewDrawMode.Normal; //Properties newly added to TreeView.... private ImageList internalStateImageList; private TreeNode topNode; private ImageList stateImageList; private Color lineColor; private string controlToolTipText = null; // Sorting private IComparer treeViewNodeSorter = null; //Events private TreeNodeMouseClickEventHandler onNodeMouseClick; private TreeNodeMouseClickEventHandler onNodeMouseDoubleClick; /// /// /// Creates a TreeView control /// public TreeView() : base() { this.treeViewState = new System.Collections.Specialized.BitVector32(TREEVIEWSTATE_showRootLines | TREEVIEWSTATE_showPlusMinus | TREEVIEWSTATE_showLines | TREEVIEWSTATE_scrollable | TREEVIEWSTATE_hideSelection); root = new TreeNode(this); // TreeView must always have an ImageIndex. SelectedImageIndexer.Index = 0; ImageIndexer.Index = 0; SetStyle(ControlStyles.UserPaint, false); SetStyle(ControlStyles.StandardClick, false); SetStyle(ControlStyles.UseTextForAccessibility, false); } ////// /// The background color for this control. Specifying null for /// this parameter sets the /// control's background color to its parent's background color. /// public override Color BackColor { get { if (ShouldSerializeBackColor()) { return base.BackColor; } else { return SystemColors.Window; } } set { base.BackColor = value; if (IsHandleCreated) { SendMessage(NativeMethods.TVM_SETBKCOLOR, 0, ColorTranslator.ToWin32(BackColor)); // This is to get around a problem in the comctl control where the lines // connecting nodes don't get the new BackColor. This messages forces // reconstruction of the line bitmaps without changing anything else. SendMessage(NativeMethods.TVM_SETINDENT, Indent, 0); } } } ////// /// ///[To be supplied.] ///[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] public override Image BackgroundImage { get { return base.BackgroundImage; } set { base.BackgroundImage = value; } } /// /// [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] new public event EventHandler BackgroundImageChanged { add { base.BackgroundImageChanged += value; } remove { base.BackgroundImageChanged -= value; } } /// /// /// ///[To be supplied.] ///[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] public override ImageLayout BackgroundImageLayout { get { return base.BackgroundImageLayout; } set { base.BackgroundImageLayout = value; } } /// /// [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] new public event EventHandler BackgroundImageLayoutChanged { add { base.BackgroundImageLayoutChanged += value; } remove { base.BackgroundImageLayoutChanged -= value; } } /// /// /// The border style of the window. /// [ SRCategory(SR.CatAppearance), DefaultValue(BorderStyle.Fixed3D), DispId(NativeMethods.ActiveX.DISPID_BORDERSTYLE), SRDescription(SR.borderStyleDescr) ] public BorderStyle BorderStyle { get { return borderStyle; } set { if (borderStyle != value) { //verify that 'value' is a valid enum type... //valid values are 0x0 to 0x2 if (!ClientUtils.IsEnumValid(value, (int)value, (int)BorderStyle.None, (int)BorderStyle.Fixed3D)) { throw new InvalidEnumArgumentException("value", (int)value, typeof(BorderStyle)); } borderStyle = value; UpdateStyles(); } } } ////// /// The value of the CheckBoxes property. The CheckBoxes /// property determines if check boxes are shown next to node in the /// tree view. /// [ SRCategory(SR.CatAppearance), DefaultValue(false), SRDescription(SR.TreeViewCheckBoxesDescr) ] public bool CheckBoxes { get { return treeViewState[TREEVIEWSTATE_checkBoxes]; } set { if (CheckBoxes != value) { treeViewState[TREEVIEWSTATE_checkBoxes] = value; if (IsHandleCreated) { // Going from true to false requires recreation if (CheckBoxes) { UpdateStyles(); } else { // Reset the Checked state after setting the checkboxes (this was Everett behavior) // The implementation of the TreeNode.Checked property has changed in Whidbey // So we need to explicit set the Checked state to false to keep the everett behavior. UpdateCheckedState(root, false); RecreateHandle(); } } } } } ////// /// ///protected override CreateParams CreateParams { [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] get { CreateParams cp = base.CreateParams; cp.ClassName = NativeMethods.WC_TREEVIEW; // V#45599 Keep the scrollbar if we are just updating styles... // if (IsHandleCreated) { int currentStyle = unchecked((int)((long)UnsafeNativeMethods.GetWindowLong(new HandleRef(this, Handle), NativeMethods.GWL_STYLE))); cp.Style |= (currentStyle & (NativeMethods.WS_HSCROLL | NativeMethods.WS_VSCROLL)); } switch (borderStyle) { case BorderStyle.Fixed3D: cp.ExStyle |= NativeMethods.WS_EX_CLIENTEDGE; break; case BorderStyle.FixedSingle: cp.Style |= NativeMethods.WS_BORDER; break; } if (!Scrollable) cp.Style |= NativeMethods.LVS_NOSCROLL; if (!HideSelection) cp.Style |= NativeMethods.TVS_SHOWSELALWAYS; if (LabelEdit) cp.Style |= NativeMethods.TVS_EDITLABELS; if (ShowLines) cp.Style |= NativeMethods.TVS_HASLINES; if (ShowPlusMinus) cp.Style |= NativeMethods.TVS_HASBUTTONS; if (ShowRootLines) cp.Style |= NativeMethods.TVS_LINESATROOT; if (HotTracking) cp.Style |= NativeMethods.TVS_TRACKSELECT; if (FullRowSelect) cp.Style |= NativeMethods.TVS_FULLROWSELECT; if (setOddHeight) { cp.Style |= NativeMethods.TVS_NONEVENHEIGHT; } // Don't set TVS_CHECKBOXES here if the window isn't created yet. // See OnHandleCreated for explanation if (ShowNodeToolTips && IsHandleCreated && !DesignMode) { cp.Style |= NativeMethods.TVS_INFOTIP; } // Don't set TVS_CHECKBOXES here if the window isn't created yet. // See OnHandleCreated for explanation if (CheckBoxes && IsHandleCreated) cp.Style |= NativeMethods.TVS_CHECKBOXES; // Don't call IsMirrored from CreateParams. That will lead to some nasty problems, since // IsMirrored ends up calling CreateParams - you dig! if (RightToLeft == RightToLeft.Yes) { if (RightToLeftLayout == true) { //We want to turn on mirroring for TreeView explicitly. cp.ExStyle |= NativeMethods.WS_EX_LAYOUTRTL; //Don't need these styles when mirroring is turned on. cp.ExStyle &= ~(NativeMethods.WS_EX_RTLREADING | NativeMethods.WS_EX_RIGHT | NativeMethods.WS_EX_LEFTSCROLLBAR); } else { cp.Style |= NativeMethods.TVS_RTLREADING; } } return cp; } } /// /// /// Deriving classes can override this to configure a default size for their control. /// This is more efficient than setting the size in the control's constructor. /// protected override Size DefaultSize { get { return new Size(121, 97); } } ////// /// This property is overridden and hidden from statement completion /// on controls that are based on Win32 Native Controls. /// [EditorBrowsable(EditorBrowsableState.Never)] protected override bool DoubleBuffered { get { return base.DoubleBuffered; } set { base.DoubleBuffered = value; } } ////// /// The current foreground color for this control, which is the /// color the control uses to draw its text. /// public override Color ForeColor { get { if (ShouldSerializeForeColor()) { return base.ForeColor; } else { return SystemColors.WindowText; } } set { base.ForeColor = value; if (IsHandleCreated) SendMessage(NativeMethods.TVM_SETTEXTCOLOR, 0, ColorTranslator.ToWin32(ForeColor)); } } ////// /// Determines whether the selection highlight spans across the width of the TreeView. /// This property will have no effect if ShowLines is true. /// [ SRCategory(SR.CatBehavior), DefaultValue(false), SRDescription(SR.TreeViewFullRowSelectDescr) ] public bool FullRowSelect { get { return treeViewState[TREEVIEWSTATE_fullRowSelect];} set { if (FullRowSelect != value) { treeViewState[TREEVIEWSTATE_fullRowSelect] = value; if (IsHandleCreated) { UpdateStyles(); } } } } ////// /// The HideSelection property specifies whether the selected node will /// be highlighted even when the TreeView loses focus. /// [ SRCategory(SR.CatBehavior), DefaultValue(true), SRDescription(SR.TreeViewHideSelectionDescr) ] public bool HideSelection { get { return treeViewState[TREEVIEWSTATE_hideSelection]; } set { if (HideSelection != value) { treeViewState[TREEVIEWSTATE_hideSelection] = value; if (IsHandleCreated) { UpdateStyles(); } } } } ////// /// The value of the HotTracking property. The HotTracking /// property determines if nodes are highlighted as the mousepointer /// passes over them. /// [ SRCategory(SR.CatBehavior), DefaultValue(false), SRDescription(SR.TreeViewHotTrackingDescr) ] public bool HotTracking { get { return treeViewState[TREEVIEWSTATE_hotTracking]; } set { if (HotTracking != value) { treeViewState[TREEVIEWSTATE_hotTracking] = value; if (IsHandleCreated) { UpdateStyles(); } } } } ////// /// The default image index for nodes in the tree view. /// [ DefaultValue(-1), SRCategory(SR.CatBehavior), Localizable(true), RefreshProperties(RefreshProperties.Repaint), TypeConverterAttribute(typeof(NoneExcludedImageIndexConverter)), Editor("System.Windows.Forms.Design.ImageIndexEditor, " + AssemblyRef.SystemDesign, typeof(UITypeEditor)), SRDescription(SR.TreeViewImageIndexDescr), RelatedImageList("ImageList") ] public int ImageIndex { get { if (imageList == null) { return -1; } if (ImageIndexer.Index >= imageList.Images.Count) { return Math.Max(0, imageList.Images.Count - 1); } return ImageIndexer.Index; } set { // If (none) is selected in the image index editor, we'll just adjust this to // mean image index 0. This is because a treeview must always have an image index - // even if no imagelist exists we want the image index to be 0. // if (value == -1) { value = 0; } if (value < 0) { throw new ArgumentOutOfRangeException("ImageIndex", SR.GetString(SR.InvalidLowBoundArgumentEx, "ImageIndex", value.ToString(CultureInfo.CurrentCulture), (0).ToString(CultureInfo.CurrentCulture))); } if (ImageIndexer.Index != value) { ImageIndexer.Index = value; if (IsHandleCreated) { RecreateHandle(); } } } } ////// /// The default image index for nodes in the tree view. /// [ SRCategory(SR.CatBehavior), Localizable(true), TypeConverterAttribute(typeof(ImageKeyConverter)), Editor("System.Windows.Forms.Design.ImageIndexEditor, " + AssemblyRef.SystemDesign, typeof(UITypeEditor)), DefaultValue(""), RefreshProperties(RefreshProperties.Repaint), SRDescription(SR.TreeViewImageKeyDescr), RelatedImageList("ImageList") ] public string ImageKey { get { return ImageIndexer.Key; } set { if (ImageIndexer.Key != value) { ImageIndexer.Key = value; if (String.IsNullOrEmpty(value) || value.Equals(SR.GetString(SR.toStringNone))) { ImageIndex = (ImageList != null) ? 0:-1; } if (IsHandleCreated) { RecreateHandle(); } } } } ////// /// Returns the image list control that is bound to the tree view. /// [ SRCategory(SR.CatBehavior), DefaultValue(null), SRDescription(SR.TreeViewImageListDescr), RefreshProperties(RefreshProperties.Repaint) ] public ImageList ImageList { get { return imageList; } set { if (value != imageList) { DetachImageListHandlers(); imageList = value; AttachImageListHandlers(); // Update TreeView's images // if (IsHandleCreated) { SendMessage(NativeMethods.TVM_SETIMAGELIST, 0, value==null? IntPtr.Zero: value.Handle); if (StateImageList != null && StateImageList.Images.Count > 0) { SendMessage(NativeMethods.TVM_SETIMAGELIST, NativeMethods.TVSIL_STATE, internalStateImageList.Handle); } } UpdateCheckedState(root, true); } } } private void AttachImageListHandlers() { if (imageList != null) { //NOTE: any handlers added here should be removed in DetachImageListHandlers imageList.RecreateHandle += new EventHandler(ImageListRecreateHandle); imageList.Disposed += new EventHandler(DetachImageList); imageList.ChangeHandle += new EventHandler(ImageListChangedHandle); } } private void DetachImageListHandlers() { if (imageList != null) { imageList.RecreateHandle -= new EventHandler(ImageListRecreateHandle); imageList.Disposed -= new EventHandler(DetachImageList); imageList.ChangeHandle -= new EventHandler(ImageListChangedHandle); } } private void AttachStateImageListHandlers() { if (stateImageList != null) { //NOTE: any handlers added here should be removed in DetachStateImageListHandlers stateImageList.RecreateHandle += new EventHandler(StateImageListRecreateHandle); stateImageList.Disposed += new EventHandler(DetachStateImageList); stateImageList.ChangeHandle += new EventHandler(StateImageListChangedHandle); } } private void DetachStateImageListHandlers() { if (stateImageList != null) { stateImageList.RecreateHandle -= new EventHandler(StateImageListRecreateHandle); stateImageList.Disposed -= new EventHandler(DetachStateImageList); stateImageList.ChangeHandle -= new EventHandler(StateImageListChangedHandle); } } ////// /// Returns the state image list control that is bound to the tree view. /// [ SRCategory(SR.CatBehavior), DefaultValue(null), SRDescription(SR.TreeViewStateImageListDescr) ] public ImageList StateImageList { get { return stateImageList; } set { if (value != stateImageList) { DetachStateImageListHandlers(); stateImageList = value; if (stateImageList != null && stateImageList.Images.Count > 0) { Image[] images = new Image[stateImageList.Images.Count + 1]; images[0] = stateImageList.Images[0]; for (int i = 1; i <= stateImageList.Images.Count ; i++) { images[i] = stateImageList.Images[i -1]; } internalStateImageList = new ImageList(); internalStateImageList.Images.AddRange(images); } AttachStateImageListHandlers(); // Update TreeView's images // if (IsHandleCreated) { if (stateImageList != null && stateImageList.Images.Count > 0) { Debug.Assert(internalStateImageList != null, "internalStateImageList is null!!"); if (internalStateImageList != null) { SendMessage(NativeMethods.TVM_SETIMAGELIST, NativeMethods.TVSIL_STATE, internalStateImageList.Handle); } } // We need to update the checks // and stateimage value for each node. UpdateCheckedState(root, true); if((value == null || stateImageList.Images.Count == 0) && CheckBoxes) { // Requires Handle Recreate to force on the checkBoxes and states.. RecreateHandle(); } else { // The TreeView shows up the state imageList after sending this message even if the nodes dont have any stateImageIndex set. // In order to avoid that we refresh nodes which would "reset" the images to none. // This causes flicker but gives us the right behavior RefreshNodes(); } } } } } ////// /// The indentation level in pixels. /// [ Localizable(true), SRCategory(SR.CatBehavior), SRDescription(SR.TreeViewIndentDescr) ] public int Indent { get { if (indent != -1) { return indent; } else if (IsHandleCreated) { return unchecked( (int) (long)SendMessage(NativeMethods.TVM_GETINDENT, 0, 0)); } return DefaultTreeViewIndent; } set { if (indent != value) { if (value < 0) { throw new ArgumentOutOfRangeException("Indent", SR.GetString(SR.InvalidLowBoundArgumentEx, "Indent", (value).ToString(CultureInfo.CurrentCulture), (0).ToString(CultureInfo.CurrentCulture))); } if (value > MaxIndent) { throw new ArgumentOutOfRangeException("Indent", SR.GetString(SR.InvalidHighBoundArgumentEx, "Indent", (value).ToString(CultureInfo.CurrentCulture), (MaxIndent).ToString(CultureInfo.CurrentCulture))); } indent = value; if (IsHandleCreated) { SendMessage(NativeMethods.TVM_SETINDENT, value, 0); indent = unchecked( (int) (long)SendMessage(NativeMethods.TVM_GETINDENT, 0, 0)); } } } } ////// /// The height of every item in the tree view, in pixels. /// [ SRCategory(SR.CatAppearance), SRDescription(SR.TreeViewItemHeightDescr) ] public int ItemHeight { get { if (itemHeight != -1) { return itemHeight; } if (IsHandleCreated) { return unchecked( (int) (long)SendMessage(NativeMethods.TVM_GETITEMHEIGHT, 0, 0)); } else { if (CheckBoxes && (DrawMode == TreeViewDrawMode.OwnerDrawAll)) { return Math.Max(16, FontHeight + 3); } return FontHeight + 3; } } set { if (itemHeight != value) { if (value < 1) { throw new ArgumentOutOfRangeException("ItemHeight", SR.GetString(SR.InvalidLowBoundArgumentEx, "ItemHeight", (value).ToString(CultureInfo.CurrentCulture), (1).ToString(CultureInfo.CurrentCulture))); } if (value >= Int16.MaxValue) { throw new ArgumentOutOfRangeException("ItemHeight", SR.GetString(SR.InvalidHighBoundArgument, "ItemHeight", (value).ToString(CultureInfo.CurrentCulture), Int16.MaxValue.ToString(CultureInfo.CurrentCulture))); } itemHeight = value; if (IsHandleCreated) { if (itemHeight % 2 != 0) { setOddHeight = true; try { RecreateHandle(); } finally { setOddHeight = false; } } SendMessage(NativeMethods.TVM_SETITEMHEIGHT, value, 0); itemHeight = unchecked( (int) (long)SendMessage(NativeMethods.TVM_GETITEMHEIGHT, 0, 0)); } } } } ////// /// The LabelEdit property determines if the label text /// of nodes in the tree view is editable. /// [ SRCategory(SR.CatBehavior), DefaultValue(false), SRDescription(SR.TreeViewLabelEditDescr) ] public bool LabelEdit { get { return treeViewState[TREEVIEWSTATE_labelEdit]; } set { if (LabelEdit != value) { treeViewState[TREEVIEWSTATE_labelEdit] = value; if (IsHandleCreated) { UpdateStyles(); } } } } ////// /// This is the color of the lines that connect the nodes of the Treeview. /// [ SRCategory(SR.CatBehavior), SRDescription(SR.TreeViewLineColorDescr), DefaultValue(typeof(Color), "Black") ] public Color LineColor { get { if (IsHandleCreated) { int intColor = unchecked((int)(long)SendMessage(NativeMethods.TVM_GETLINECOLOR, 0, 0)); return ColorTranslator.FromWin32(intColor); } return lineColor; } set { if (lineColor != value) { lineColor = value; if (IsHandleCreated) { SendMessage(NativeMethods.TVM_SETLINECOLOR, 0, ColorTranslator.ToWin32(lineColor)); } } } } ////// /// The collection of nodes associated with this TreeView control /// [ SRCategory(SR.CatBehavior), DesignerSerializationVisibility(DesignerSerializationVisibility.Content), Localizable(true), SRDescription(SR.TreeViewNodesDescr), MergableProperty(false) ] public TreeNodeCollection Nodes { get { if (nodes == null) { nodes = new TreeNodeCollection(root); } return nodes; } } ////// /// [ SRCategory(SR.CatBehavior), DefaultValue(TreeViewDrawMode.Normal), SRDescription(SR.TreeViewDrawModeDescr) ] public TreeViewDrawMode DrawMode { get { return drawMode; } set { //valid values are 0x0 to 0x2 if (!ClientUtils.IsEnumValid(value, (int)value, (int)TreeViewDrawMode.Normal, (int)TreeViewDrawMode.OwnerDrawAll)) { throw new InvalidEnumArgumentException("value", (int)value, typeof(TreeViewDrawMode)); } if (drawMode != value) { drawMode = value; Invalidate(); // We need to invalidate when the Control resizes when the we support custom draw. if (DrawMode == TreeViewDrawMode.OwnerDrawAll) { SetStyle(ControlStyles.ResizeRedraw, true); } } } } ////// Indicates the drawing mode for the tree view. /// ////// /// The delimeter string used by TreeNode.getFullPath(). /// [ SRCategory(SR.CatBehavior), DefaultValue("\\"), SRDescription(SR.TreeViewPathSeparatorDescr) ] public string PathSeparator { get { return pathSeparator; } set { pathSeparator = value; } } ////// /// [ Browsable(false), EditorBrowsable(EditorBrowsableState.Never), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) ] public new Padding Padding { get { return base.Padding; } set { base.Padding = value;} } [ Browsable(false), EditorBrowsable(EditorBrowsableState.Never) ] public new event EventHandler PaddingChanged { add { base.PaddingChanged += value; } remove { base.PaddingChanged -= value; } } ////// ///[To be supplied.] ////// /// This is used for international applications where the language /// is written from RightToLeft. When this property is true, // and the RightToLeft is true, mirroring will be turned on on the form, and /// control placement and text will be from right to left. /// [ SRCategory(SR.CatAppearance), Localizable(true), DefaultValue(false), SRDescription(SR.ControlRightToLeftLayoutDescr) ] public virtual bool RightToLeftLayout { get { return rightToLeftLayout; } set { if (value != rightToLeftLayout) { rightToLeftLayout = value; using(new LayoutTransaction(this, this, PropertyNames.RightToLeftLayout)) { OnRightToLeftLayoutChanged(EventArgs.Empty); } } } } ////// /// [ SRCategory(SR.CatBehavior), DefaultValue(true), SRDescription(SR.TreeViewScrollableDescr) ] public bool Scrollable { get { return treeViewState[TREEVIEWSTATE_scrollable]; } set { if (Scrollable != value) { treeViewState[TREEVIEWSTATE_scrollable] = value; RecreateHandle(); } } } ///[To be supplied.] ////// /// The image index that a node will display when selected. /// The index applies to the ImageList referred to by the imageList property, /// [ DefaultValue(-1), SRCategory(SR.CatBehavior), TypeConverterAttribute(typeof(NoneExcludedImageIndexConverter)), Localizable(true), Editor("System.Windows.Forms.Design.ImageIndexEditor, " + AssemblyRef.SystemDesign, typeof(UITypeEditor)), SRDescription(SR.TreeViewSelectedImageIndexDescr), RelatedImageList("ImageList") ] public int SelectedImageIndex { get { if (imageList == null) { return -1; } if (SelectedImageIndexer.Index >= imageList.Images.Count) { return Math.Max(0, imageList.Images.Count - 1); } return SelectedImageIndexer.Index; } set { // If (none) is selected in the image index editor, we'll just adjust this to // mean image index 0. This is because a treeview must always have an image index - // even if no imagelist exists we want the image index to be 0. // if (value == -1) { value = 0; } if (value < 0) { throw new ArgumentOutOfRangeException("SelectedImageIndex", SR.GetString(SR.InvalidLowBoundArgumentEx, "SelectedImageIndex", value.ToString(CultureInfo.CurrentCulture), (0).ToString(CultureInfo.CurrentCulture))); } if (SelectedImageIndexer.Index != value) { SelectedImageIndexer.Index = value; if (IsHandleCreated) { RecreateHandle(); } } } } ////// /// The default image index for nodes in the tree view. /// [ SRCategory(SR.CatBehavior), Localizable(true), TypeConverterAttribute(typeof(ImageKeyConverter)), Editor("System.Windows.Forms.Design.ImageIndexEditor, " + AssemblyRef.SystemDesign, typeof(UITypeEditor)), DefaultValue(""), RefreshProperties(RefreshProperties.Repaint), SRDescription(SR.TreeViewSelectedImageKeyDescr), RelatedImageList("ImageList") ] public string SelectedImageKey { get { return SelectedImageIndexer.Key; } set { if (SelectedImageIndexer.Key != value) { SelectedImageIndexer.Key = value; if (String.IsNullOrEmpty(value) || value.Equals(SR.GetString(SR.toStringNone))) { SelectedImageIndex = (ImageList != null) ? 0 : -1; } if (IsHandleCreated) { RecreateHandle(); } } } } ////// /// The currently selected tree node, or null if nothing is selected. /// [ SRCategory(SR.CatAppearance), Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), SRDescription(SR.TreeViewSelectedNodeDescr) ] public TreeNode SelectedNode { get { if (IsHandleCreated) { Debug.Assert(selectedNode == null || selectedNode.TreeView != this, "handle is created, but we're still caching selectedNode"); IntPtr hItem = SendMessage(NativeMethods.TVM_GETNEXTITEM, NativeMethods.TVGN_CARET, 0); if (hItem == IntPtr.Zero) return null; return NodeFromHandle(hItem); } else if (selectedNode != null && selectedNode.TreeView == this) { return selectedNode; } else { return null; } } set { if (IsHandleCreated && (value == null || value.TreeView == this)) { // This class invariant is not quite correct -- if the selected node does not belong to this Treeview, // selectedNode != null even though the handle is created. We will call set_SelectedNode // to inform the handle that the selected node has been added to the TreeView. Debug.Assert(selectedNode == null || selectedNode.TreeView != this, "handle is created, but we're still caching selectedNode"); IntPtr hnode = (value == null ? IntPtr.Zero : value.Handle); SendMessage(NativeMethods.TVM_SELECTITEM, NativeMethods.TVGN_CARET, hnode); selectedNode = null; } else { selectedNode = value; } } } ////// /// The ShowLines property determines if lines are drawn between /// nodes in the tree view. /// [ SRCategory(SR.CatBehavior), DefaultValue(true), SRDescription(SR.TreeViewShowLinesDescr) ] public bool ShowLines { get { return treeViewState[TREEVIEWSTATE_showLines]; } set { if (ShowLines != value) { treeViewState[TREEVIEWSTATE_showLines] = value; if (IsHandleCreated) { UpdateStyles(); } } } } ////// /// The ShowLines property determines whether or not the tooltips willbe displayed on the nodes /// [ SRCategory(SR.CatBehavior), DefaultValue(false), SRDescription(SR.TreeViewShowShowNodeToolTipsDescr) ] public bool ShowNodeToolTips { get { return treeViewState[TREEVIEWSTATE_showNodeToolTips]; } set { if (ShowNodeToolTips != value) { treeViewState[TREEVIEWSTATE_showNodeToolTips] = value; if (ShowNodeToolTips) RecreateHandle(); } } } ////// /// The ShowPlusMinus property determines if the "plus/minus" /// expand button is shown next to tree nodes that have children. /// [ SRCategory(SR.CatBehavior), DefaultValue(true), SRDescription(SR.TreeViewShowPlusMinusDescr) ] public bool ShowPlusMinus { get { return treeViewState[TREEVIEWSTATE_showPlusMinus]; } set { if (ShowPlusMinus != value) { treeViewState[TREEVIEWSTATE_showPlusMinus] = value; if (IsHandleCreated) { UpdateStyles(); } } } } ////// /// Determines if lines are draw between nodes at the root of /// the tree view. /// [ SRCategory(SR.CatBehavior), DefaultValue(true), SRDescription(SR.TreeViewShowRootLinesDescr) ] public bool ShowRootLines { get { return treeViewState[TREEVIEWSTATE_showRootLines];} set { if (ShowRootLines != value) { treeViewState[TREEVIEWSTATE_showRootLines] = value; if (IsHandleCreated) { UpdateStyles(); } } } } ////// /// The Sorted property determines if nodes in the tree view are sorted. /// [ SRCategory(SR.CatBehavior), DefaultValue(false), SRDescription(SR.TreeViewSortedDescr), Browsable(false), EditorBrowsable(EditorBrowsableState.Never) ] public bool Sorted { get { return treeViewState[TREEVIEWSTATE_sorted]; } set { if (Sorted != value) { treeViewState[TREEVIEWSTATE_sorted] = value; if (Sorted && TreeViewNodeSorter == null && Nodes.Count >= 1) { RefreshNodes(); } } } } ////// /// The sorting comparer for this TreeView. /// [ SRCategory(SR.CatBehavior), Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), SRDescription(SR.TreeViewNodeSorterDescr) ] public IComparer TreeViewNodeSorter { get { return treeViewNodeSorter; } set { if (treeViewNodeSorter != value) { treeViewNodeSorter = value; if (value != null) { Sort(); } } } } ////// /// [Browsable(false), EditorBrowsable(EditorBrowsableState.Never), Bindable(false)] public override string Text { get { return base.Text; } set { base.Text = value; } } ///[To be supplied.] ////// [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] new public event EventHandler TextChanged { add { base.TextChanged += value; } remove { base.TextChanged -= value; } } /// /// /// The first visible node in the TreeView. Initially /// the first root node is at the top of the TreeView, but if the /// contents have been scrolled another node may be at the top. /// [ SRCategory(SR.CatAppearance), Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), SRDescription(SR.TreeViewTopNodeDescr) ] public TreeNode TopNode { get { if (IsHandleCreated) { IntPtr hitem = SendMessage(NativeMethods.TVM_GETNEXTITEM, NativeMethods.TVGN_FIRSTVISIBLE, 0); return(hitem == IntPtr.Zero ? null : NodeFromHandle(hitem)); } return topNode; } set { if (IsHandleCreated && (value == null || value.TreeView == this)) { // This class invariant is not quite correct -- if the selected node does not belong to this Treeview, // selectedNode != null even though the handle is created. We will call set_SelectedNode // to inform the handle that the selected node has been added to the TreeView. Debug.Assert(topNode == null || topNode.TreeView != this, "handle is created, but we're still caching selectedNode"); IntPtr hnode = (value == null ? IntPtr.Zero : value.Handle); SendMessage(NativeMethods.TVM_SELECTITEM, NativeMethods.TVGN_FIRSTVISIBLE, hnode); topNode = null; } else { topNode = value; } } } ////// /// The count of fully visible nodes in the tree view. This number /// may be greater than the number of nodes in the control. /// The control calculates this value by dividing the height of the /// client window by the height of an item /// [ SRCategory(SR.CatAppearance), Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), SRDescription(SR.TreeViewVisibleCountDescr) ] public int VisibleCount { get { if (IsHandleCreated) return unchecked( (int) (long)SendMessage(NativeMethods.TVM_GETVISIBLECOUNT, 0, 0)); return 0; } } ////// /// [SRCategory(SR.CatBehavior), SRDescription(SR.TreeViewBeforeEditDescr)] public event NodeLabelEditEventHandler BeforeLabelEdit { add { onBeforeLabelEdit += value; } remove { onBeforeLabelEdit -= value; } } ///[To be supplied.] ////// /// [SRCategory(SR.CatBehavior), SRDescription(SR.TreeViewAfterEditDescr)] public event NodeLabelEditEventHandler AfterLabelEdit { add { onAfterLabelEdit += value; } remove { onAfterLabelEdit -= value; } } ///[To be supplied.] ////// /// [SRCategory(SR.CatBehavior), SRDescription(SR.TreeViewBeforeCheckDescr)] public event TreeViewCancelEventHandler BeforeCheck { add { onBeforeCheck += value; } remove { onBeforeCheck -= value; } } ///[To be supplied.] ////// /// [SRCategory(SR.CatBehavior), SRDescription(SR.TreeViewAfterCheckDescr)] public event TreeViewEventHandler AfterCheck { add { onAfterCheck += value; } remove { onAfterCheck -= value; } } ///[To be supplied.] ////// /// [SRCategory(SR.CatBehavior), SRDescription(SR.TreeViewBeforeCollapseDescr)] public event TreeViewCancelEventHandler BeforeCollapse { add { onBeforeCollapse += value; } remove { onBeforeCollapse -= value; } } ///[To be supplied.] ////// /// [SRCategory(SR.CatBehavior), SRDescription(SR.TreeViewAfterCollapseDescr)] public event TreeViewEventHandler AfterCollapse { add { onAfterCollapse += value; } remove { onAfterCollapse -= value; } } ///[To be supplied.] ////// /// [SRCategory(SR.CatBehavior), SRDescription(SR.TreeViewBeforeExpandDescr)] public event TreeViewCancelEventHandler BeforeExpand { add { onBeforeExpand += value; } remove { onBeforeExpand -= value; } } ///[To be supplied.] ////// /// [SRCategory(SR.CatBehavior), SRDescription(SR.TreeViewAfterExpandDescr)] public event TreeViewEventHandler AfterExpand { add { onAfterExpand += value; } remove { onAfterExpand -= value; } } ///[To be supplied.] ////// /// [SRCategory(SR.CatBehavior), SRDescription(SR.TreeViewDrawNodeEventDescr)] public event DrawTreeNodeEventHandler DrawNode { add { onDrawNode += value; } remove { onDrawNode -= value; } } ///Fires when a TreeView node needs to be drawn. ////// /// [SRCategory(SR.CatAction), SRDescription(SR.ListViewItemDragDescr)] public event ItemDragEventHandler ItemDrag { add { onItemDrag += value; } remove { onItemDrag -= value; } } ///[To be supplied.] ////// /// [SRCategory(SR.CatAction), SRDescription(SR.TreeViewNodeMouseHoverDescr)] public event TreeNodeMouseHoverEventHandler NodeMouseHover { add { onNodeMouseHover += value; } remove { onNodeMouseHover -= value; } } ///[To be supplied.] ////// /// [SRCategory(SR.CatBehavior), SRDescription(SR.TreeViewBeforeSelectDescr)] public event TreeViewCancelEventHandler BeforeSelect { add { onBeforeSelect += value; } remove { onBeforeSelect -= value; } } ///[To be supplied.] ////// /// [SRCategory(SR.CatBehavior), SRDescription(SR.TreeViewAfterSelectDescr)] public event TreeViewEventHandler AfterSelect { add { onAfterSelect += value; } remove { onAfterSelect -= value; } } ///[To be supplied.] ////// /// TreeView Onpaint. /// ///[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] public new event PaintEventHandler Paint { add { base.Paint += value; } remove { base.Paint -= value; } } /// /// /// [SRCategory(SR.CatBehavior), SRDescription(SR.TreeViewNodeMouseClickDescr)] public event TreeNodeMouseClickEventHandler NodeMouseClick { add { onNodeMouseClick += value; } remove { onNodeMouseClick -= value; } } ///[To be supplied.] ////// /// [SRCategory(SR.CatBehavior), SRDescription(SR.TreeViewNodeMouseDoubleClickDescr)] public event TreeNodeMouseClickEventHandler NodeMouseDoubleClick { add { onNodeMouseDoubleClick += value; } remove { onNodeMouseDoubleClick -= value; } } ///[To be supplied.] ////// /// [SRCategory(SR.CatPropertyChanged), SRDescription(SR.ControlOnRightToLeftLayoutChangedDescr)] public event EventHandler RightToLeftLayoutChanged { add { onRightToLeftLayoutChanged += value; } remove { onRightToLeftLayoutChanged -= value; } } ///[To be supplied.] ////// /// Disables redrawing of the tree view. A call to beginUpdate() must be /// balanced by a following call to endUpdate(). Following a call to /// beginUpdate(), any redrawing caused by operations performed on the /// tree view is deferred until the call to endUpdate(). /// public void BeginUpdate() { BeginUpdateInternal(); } ////// /// Collapses all nodes at the root level. /// public void CollapseAll() { root.Collapse(); } ////// /// ///protected override void CreateHandle() { if (!RecreatingHandle) { IntPtr userCookie = UnsafeNativeMethods.ThemingScope.Activate(); try { NativeMethods.INITCOMMONCONTROLSEX icc = new NativeMethods.INITCOMMONCONTROLSEX(); icc.dwICC = NativeMethods.ICC_TREEVIEW_CLASSES; SafeNativeMethods.InitCommonControlsEx(icc); } finally { UnsafeNativeMethods.ThemingScope.Deactivate(userCookie); } } base.CreateHandle(); } /// /// /// Resets the imageList to null. We wire this method up to the imageList's /// Dispose event, so that we don't hang onto an imageList that's gone away. /// ///private void DetachImageList(object sender, EventArgs e) { ImageList = null; } /// /// /// Resets the stateimageList to null. We wire this method up to the stateimageList's /// Dispose event, so that we don't hang onto an stateimageList that's gone away. /// ///private void DetachStateImageList(object sender, EventArgs e) { internalStateImageList = null; StateImageList = null; } /// /// /// ///protected override void Dispose(bool disposing) { if (disposing) { foreach (TreeNode node in Nodes) { node.ContextMenu = null; } // lock(this) { DetachImageListHandlers(); imageList = null; DetachStateImageListHandlers(); stateImageList = null; } } base.Dispose(disposing); } /// /// /// Reenables redrawing of the tree view. A call to beginUpdate() must be /// balanced by a following call to endUpdate(). Following a call to /// beginUpdate(), any redrawing caused by operations performed on the /// combo box is deferred until the call to endUpdate(). /// public void EndUpdate() { EndUpdateInternal(); } ////// /// Expands all nodes at the root level. /// public void ExpandAll() { root.ExpandAll(); } ////// /// Forces the TreeView to recalculate all its nodes widths so that it updates the /// scrollbars as appropriate. /// ///internal void ForceScrollbarUpdate(bool delayed) { // ForceScrollbarUpdate call WM_SETREDRAW( FALSE ) followed by WM_SETREDRAW( TRUE ) // So if TreeView.BeginUpdate is called // ForceScrollbarUpdate effectively causes tree view to ignore BeginUpdate and cause control to update on every change. // So gaurd against this scenario by using the new internal method on Control. if (!IsUpdating()) { if (IsHandleCreated) { SendMessage(NativeMethods.WM_SETREDRAW, 0, 0); if (delayed) UnsafeNativeMethods.PostMessage(new HandleRef(this, Handle), NativeMethods.WM_SETREDRAW, (IntPtr)1, IntPtr.Zero); else SendMessage(NativeMethods.WM_SETREDRAW, 1, 0); } } } /// /// /// Called by ToolTip to poke in that Tooltip into this ComCtl so that the Native ChildToolTip is not exposed. /// ///internal void SetToolTip(ToolTip toolTip, string toolTipText) { if (toolTip != null) { UnsafeNativeMethods.SendMessage(new HandleRef(toolTip, toolTip.Handle), NativeMethods.TTM_SETMAXTIPWIDTH, 0, SystemInformation.MaxWindowTrackSize.Width); UnsafeNativeMethods.SendMessage(new HandleRef(this, this.Handle), NativeMethods.TVM_SETTOOLTIPS, new HandleRef(toolTip, toolTip.Handle), 0); controlToolTipText = toolTipText; } } /// /// /// Gives the information about which part of the treeNode is at the given point. /// public TreeViewHitTestInfo HitTest(Point pt) { return HitTest(pt.X, pt.Y); } ////// /// Gives the information about which part of the treeNode is at the given x, y. /// public TreeViewHitTestInfo HitTest(int x, int y) { NativeMethods.TV_HITTESTINFO tvhi = new NativeMethods.TV_HITTESTINFO(); tvhi.pt_x = x; tvhi.pt_y = y; IntPtr hnode = UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TVM_HITTEST, 0, tvhi); TreeNode node = (hnode == IntPtr.Zero ? null : NodeFromHandle(hnode)); TreeViewHitTestLocations loc = (TreeViewHitTestLocations)tvhi.flags; return (new TreeViewHitTestInfo(node, loc)); } ////// /// Defined so that a tree node can use it /// /// ///internal bool TreeViewBeforeCheck(TreeNode node, TreeViewAction actionTaken) { TreeViewCancelEventArgs tvce = new TreeViewCancelEventArgs(node, false, actionTaken); OnBeforeCheck(tvce); return (tvce.Cancel); } internal void TreeViewAfterCheck(TreeNode node, TreeViewAction actionTaken) { OnAfterCheck(new TreeViewEventArgs(node, actionTaken)); } /// /// /// Returns count of nodes at root, optionally including all subtrees. /// public int GetNodeCount(bool includeSubTrees) { return root.GetNodeCount(includeSubTrees); } ////// /// Returns the TreeNode at the given location in tree view coordinates. /// public TreeNode GetNodeAt(Point pt) { return GetNodeAt(pt.X, pt.Y); } ////// /// Returns the TreeNode at the given location in tree view coordinates. /// public TreeNode GetNodeAt(int x, int y) { NativeMethods.TV_HITTESTINFO tvhi = new NativeMethods.TV_HITTESTINFO(); tvhi.pt_x = x; tvhi.pt_y = y; IntPtr hnode = UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TVM_HITTEST, 0, tvhi); return(hnode == IntPtr.Zero ? null : NodeFromHandle(hnode)); } private void ImageListRecreateHandle(object sender, EventArgs e) { if (IsHandleCreated) { IntPtr handle = (ImageList == null) ? IntPtr.Zero : ImageList.Handle; SendMessage(NativeMethods.TVM_SETIMAGELIST, 0, handle); } } private void UpdateImagesRecursive ( TreeNode node ) { node.UpdateImage(); // VSWhidbey 208200: Iterate only through the Nodes collection rather than the // array since an item might have been removed from the collection, and // correspondingly "removed" from the array, but still exist in the array // since the array isn't actually re-dimensioned down to a smaller size. foreach (TreeNode child in node.Nodes) { UpdateImagesRecursive(child); } } private void ImageListChangedHandle(object sender, EventArgs e) { if ((null != sender) && (sender == imageList) && IsHandleCreated) { BeginUpdate(); foreach (TreeNode node in Nodes) { UpdateImagesRecursive(node); } EndUpdate(); } } private void StateImageListRecreateHandle(object sender, EventArgs e) { if (IsHandleCreated) { IntPtr handle = IntPtr.Zero; if (internalStateImageList != null) { handle = internalStateImageList.Handle; } SendMessage(NativeMethods.TVM_SETIMAGELIST, NativeMethods.TVSIL_STATE, handle); } } private void StateImageListChangedHandle(object sender, EventArgs e) { if ((null != sender) && (sender == stateImageList) && IsHandleCreated) { // Since the native treeview requires the state imagelist to be 1-indexed we need to // re add the images if the original collection had changed. if (stateImageList != null && stateImageList.Images.Count > 0) { Image[] images = new Image[stateImageList.Images.Count + 1]; images[0] = stateImageList.Images[0]; for (int i = 1; i <= stateImageList.Images.Count ; i++) { images[i] = stateImageList.Images[i -1]; } if (internalStateImageList != null) { internalStateImageList.Images.Clear(); internalStateImageList.Images.AddRange(images); } else { internalStateImageList = new ImageList(); internalStateImageList.Images.AddRange(images); } Debug.Assert(internalStateImageList != null, "Why are changing images when the Imagelist is null?"); if (internalStateImageList != null) { SendMessage(NativeMethods.TVM_SETIMAGELIST, NativeMethods.TVSIL_STATE, internalStateImageList.Handle); } } else //stateImageList == null || stateImageList.Images.Count = 0; { UpdateCheckedState(root, true); } } } ////// /// /// protected override bool IsInputKey(Keys keyData) { // If in edit mode, treat Return as an input key, so the form doesn't grab it // and treat it as clicking the Form.AcceptButton. Similarly for Escape // and Form.CancelButton. if (editNode != null && (keyData & Keys.Alt) == 0) { switch (keyData & Keys.KeyCode) { case Keys.Return: case Keys.Escape: case Keys.PageUp: case Keys.PageDown: case Keys.Home: case Keys.End: return true; } } return base.IsInputKey(keyData); } ////// Overridden to handle RETURN key. /// ////// /// Note this can be null - particularly if any windows messages get generated during /// the insertion of a tree node (TVM_INSERTITEM) /// ///internal TreeNode NodeFromHandle(IntPtr handle) { TreeNode node = (TreeNode)nodeTable[handle]; return node; } /// /// /// Fires the DrawNode event. /// protected virtual void OnDrawNode(DrawTreeNodeEventArgs e) { if (onDrawNode != null) onDrawNode(this, e); } ////// /// ///protected override void OnHandleCreated(EventArgs e) { TreeNode savedSelectedNode = this.selectedNode; this.selectedNode = null; base.OnHandleCreated(e); //ComCtl 5 has some bug fixes that, to enable, require us to send the control //a CCM_SETVERSION with 5 as the version. The one we want in particular is //the fix for the node clipping issue when a font is set by means of CDRF_NEWFONT. //The fix is not perfect, but the behavior is better than before. int version = unchecked((int)(long)SendMessage(NativeMethods.CCM_GETVERSION, 0, 0)); if (version < 5) { SendMessage(NativeMethods.CCM_SETVERSION, 5, 0); } // Workaround for problem in TreeView where it doesn't recognize the TVS_CHECKBOXES // style if it is set before the window is created. To get around the problem, // we set it here after the window is created, and we make sure we don't set it // in getCreateParams so that this will actually change the value of the bit. // This seems to make the Treeview happy. if (CheckBoxes) { int style = unchecked((int)(UnsafeNativeMethods.GetWindowLong(new HandleRef(this, Handle), NativeMethods.GWL_STYLE))); style |= NativeMethods.TVS_CHECKBOXES; UnsafeNativeMethods.SetWindowLong(new HandleRef(this, Handle), NativeMethods.GWL_STYLE, new HandleRef(null, (IntPtr)style)); } if (ShowNodeToolTips && !DesignMode) { int style = unchecked((int)(UnsafeNativeMethods.GetWindowLong(new HandleRef(this, Handle), NativeMethods.GWL_STYLE))); style |= NativeMethods.TVS_INFOTIP; UnsafeNativeMethods.SetWindowLong(new HandleRef(this, Handle), NativeMethods.GWL_STYLE, new HandleRef(null, (IntPtr)style)); } Color c; c = BackColor; if (c != SystemColors.Window) SendMessage(NativeMethods.TVM_SETBKCOLOR, 0, ColorTranslator.ToWin32(c)); c = ForeColor; if (c != SystemColors.WindowText) SendMessage(NativeMethods.TVM_SETTEXTCOLOR, 0, ColorTranslator.ToWin32(c)); /// put the linecolor into the native control only if Set ... if (lineColor != Color.Empty) { SendMessage(NativeMethods.TVM_SETLINECOLOR, 0, ColorTranslator.ToWin32(lineColor)); } if (imageList != null) SendMessage(NativeMethods.TVM_SETIMAGELIST, 0, imageList.Handle); if (stateImageList != null && stateImageList.Images.Count > 0) { Image[] images = new Image[stateImageList.Images.Count + 1]; images[0] = stateImageList.Images[0]; for (int i = 1; i <= stateImageList.Images.Count ; i++) { images[i] = stateImageList.Images[i -1]; } internalStateImageList = new ImageList(); internalStateImageList.Images.AddRange(images); SendMessage(NativeMethods.TVM_SETIMAGELIST, NativeMethods.TVSIL_STATE, internalStateImageList.Handle); } if (indent != -1) { SendMessage(NativeMethods.TVM_SETINDENT, indent, 0); } if (itemHeight != -1) { SendMessage(NativeMethods.TVM_SETITEMHEIGHT, ItemHeight, 0); } // Fix for bug 331158. Essentially we are setting the width to be infinite so that the // TreeView never thinks it needs a scrollbar when the first node is created // during the first handle creation. // // This is set back to the oldSize after the Realize method. int oldSize = 0; try { treeViewState[TREEVIEWSTATE_stopResizeWindowMsgs] = true; oldSize = this.Width; int flags = NativeMethods.SWP_NOZORDER | NativeMethods.SWP_NOACTIVATE | NativeMethods.SWP_NOMOVE; SafeNativeMethods.SetWindowPos(new HandleRef(this, this.Handle), NativeMethods.NullHandleRef, this.Left, this.Top, Int32.MaxValue, this.Height, flags); root.Realize(false); if (oldSize != 0) { SafeNativeMethods.SetWindowPos(new HandleRef(this, this.Handle), NativeMethods.NullHandleRef, this.Left, this.Top, oldSize, this.Height, flags); } } finally { treeViewState[TREEVIEWSTATE_stopResizeWindowMsgs] = false; } SelectedNode = savedSelectedNode; } /// /// /// ///protected override void OnHandleDestroyed(EventArgs e) { selectedNode = SelectedNode; base.OnHandleDestroyed(e); } /// /// /// We keep track of if we've hovered already so we don't fire multiple hover events /// ///protected override void OnMouseLeave(EventArgs e) { hoveredAlready = false; base.OnMouseLeave(e); } /// /// /// In order for the MouseHover event to fire for each item in a TreeView, /// the node the mouse is hovering over is found. Each time a new node is hovered /// over a new event is raised. /// protected override void OnMouseHover(EventArgs e) { /// Hover events need to be caught for each node /// within the TreeView so the appropriate /// NodeHovered event can be raised. NativeMethods.TV_HITTESTINFO tvhip = new NativeMethods.TV_HITTESTINFO(); Point pos = Cursor.Position; pos = PointToClientInternal(pos); tvhip.pt_x = pos.X; tvhip.pt_y = pos.Y; IntPtr hnode = UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TVM_HITTEST, 0, tvhip); if (hnode != IntPtr.Zero && ((tvhip.flags & NativeMethods.TVHT_ONITEM) != 0)) { TreeNode tn = NodeFromHandle(hnode); if (tn != prevHoveredNode && tn != null) { OnNodeMouseHover( new TreeNodeMouseHoverEventArgs(tn)); prevHoveredNode = tn; } } if (!hoveredAlready) { base.OnMouseHover(e); hoveredAlready = true; } ResetMouseEventArgs(); } ////// /// Fires the beforeLabelEdit event. /// protected virtual void OnBeforeLabelEdit(NodeLabelEditEventArgs e) { if (onBeforeLabelEdit != null) onBeforeLabelEdit(this, e); } ////// /// Fires the afterLabelEdit event. /// protected virtual void OnAfterLabelEdit(NodeLabelEditEventArgs e) { if (onAfterLabelEdit != null) onAfterLabelEdit(this, e); } ////// /// Fires the beforeCheck event. /// protected virtual void OnBeforeCheck(TreeViewCancelEventArgs e) { if (onBeforeCheck != null) onBeforeCheck(this, e); } ////// /// Fires the afterCheck event. /// protected virtual void OnAfterCheck(TreeViewEventArgs e) { if (onAfterCheck != null) onAfterCheck(this, e); } ////// /// Fires the beforeCollapse event. /// protected internal virtual void OnBeforeCollapse(TreeViewCancelEventArgs e) { if (onBeforeCollapse != null) onBeforeCollapse(this, e); } ////// /// Fires the afterCollapse event. /// protected internal virtual void OnAfterCollapse(TreeViewEventArgs e) { if (onAfterCollapse != null) onAfterCollapse(this, e); } ////// /// Fires the beforeExpand event. /// protected virtual void OnBeforeExpand(TreeViewCancelEventArgs e) { if (onBeforeExpand != null) onBeforeExpand(this, e); } ////// /// Fires the afterExpand event. /// protected virtual void OnAfterExpand(TreeViewEventArgs e) { if (onAfterExpand != null) onAfterExpand(this, e); } ////// /// Fires the ItemDrag event. /// protected virtual void OnItemDrag(ItemDragEventArgs e) { if (onItemDrag != null) onItemDrag(this, e); } ////// /// Fires the NodeMouseHover event. /// protected virtual void OnNodeMouseHover(TreeNodeMouseHoverEventArgs e) { if (onNodeMouseHover != null) onNodeMouseHover(this, e); } ////// /// Fires the beforeSelect event. /// protected virtual void OnBeforeSelect(TreeViewCancelEventArgs e) { if (onBeforeSelect != null) onBeforeSelect(this, e); } ////// /// Fires the afterSelect event. /// protected virtual void OnAfterSelect(TreeViewEventArgs e) { if (onAfterSelect != null) onAfterSelect(this, e); } ////// /// Fires the onNodeMouseClick event. /// protected virtual void OnNodeMouseClick(TreeNodeMouseClickEventArgs e) { if (onNodeMouseClick != null) onNodeMouseClick(this, e); } ////// /// Fires the onNodeMouseDoubleClick event. /// protected virtual void OnNodeMouseDoubleClick(TreeNodeMouseClickEventArgs e) { if (onNodeMouseDoubleClick != null) onNodeMouseDoubleClick(this, e); } ////// /// Handles the OnBeforeCheck / OnAfterCheck for keyboard clicks /// ///protected override void OnKeyDown(KeyEventArgs e) { base.OnKeyDown(e); if (e.Handled) return; // if it's a space, send the check notifications and toggle the checkbox if we're not // cancelled. if (CheckBoxes && (e.KeyData & Keys.KeyCode) == Keys.Space) { TreeNode node = this.SelectedNode; if (node != null) { bool eventReturn = TreeViewBeforeCheck(node, TreeViewAction.ByKeyboard); if (!eventReturn) { node.CheckedInternal = !node.CheckedInternal; TreeViewAfterCheck(node, TreeViewAction.ByKeyboard); } e.Handled = true; return; } } } /// /// /// Handles the OnBeforeCheck / OnAfterCheck for keyboard clicks /// ///protected override void OnKeyUp(KeyEventArgs e) { base.OnKeyUp(e); if (e.Handled) return; // eat the space key if ((e.KeyData & Keys.KeyCode) == Keys.Space) { e.Handled = true; return; } } /// /// /// Handles the OnBeforeCheck / OnAfterCheck for keyboard clicks /// ///protected override void OnKeyPress(KeyPressEventArgs e) { base.OnKeyPress(e); if (e.Handled) return; // eat the space key if (e.KeyChar == ' ') e.Handled = true; } /// /// /// [EditorBrowsable(EditorBrowsableState.Advanced)] protected virtual void OnRightToLeftLayoutChanged(EventArgs e) { if (GetAnyDisposingInHierarchy()) { return; } if (RightToLeft == RightToLeft.Yes) { RecreateHandle(); } if (onRightToLeftLayoutChanged != null) { onRightToLeftLayoutChanged(this, e); } } // Refresh the nodes by clearing the tree and adding the nodes back again // private void RefreshNodes() { TreeNode[] nodes = new TreeNode[Nodes.Count]; Nodes.CopyTo(nodes, 0); Nodes.Clear(); Nodes.AddRange(nodes); } ///[To be supplied.] ////// /// This resets the indentation to the system default. /// private void ResetIndent() { indent = -1; // is this overkill? RecreateHandle(); } ////// /// This resets the item height to the system default. /// private void ResetItemHeight() { itemHeight = -1; RecreateHandle(); } ////// /// Retrieves true if the indent should be persisted in code gen. /// private bool ShouldSerializeIndent() { return(indent != -1); } ////// /// Retrieves true if the itemHeight should be persisted in code gen. /// private bool ShouldSerializeItemHeight() { return(itemHeight != -1); } private bool ShouldSerializeSelectedImageIndex() { if (imageList != null) { return (SelectedImageIndex != 0); } return (SelectedImageIndex != -1); } private bool ShouldSerializeImageIndex() { if (imageList != null) { return (ImageIndex != 0); } return (ImageIndex != -1); } ////// /// Updated the sorted order /// public void Sort() { Sorted = true; RefreshNodes(); } ////// /// Returns a string representation for this control. /// ///public override string ToString() { string s = base.ToString(); if (Nodes != null) { s += ", Nodes.Count: " + Nodes.Count.ToString(CultureInfo.CurrentCulture); if (Nodes.Count > 0) s += ", Nodes[0]: " + Nodes[0].ToString(); } return s; } private unsafe void TvnBeginDrag(MouseButtons buttons, NativeMethods.NMTREEVIEW* nmtv) { NativeMethods.TV_ITEM item = nmtv->itemNew; // Check for invalid node handle if (item.hItem == IntPtr.Zero) { return; } TreeNode node = NodeFromHandle(item.hItem); OnItemDrag(new ItemDragEventArgs(buttons, node)); } private unsafe IntPtr TvnExpanding(NativeMethods.NMTREEVIEW* nmtv) { NativeMethods.TV_ITEM item = nmtv->itemNew; // Check for invalid node handle if (item.hItem == IntPtr.Zero) { return IntPtr.Zero; } TreeViewCancelEventArgs e = null; if ((item.state & NativeMethods.TVIS_EXPANDED) == 0) { e = new TreeViewCancelEventArgs(NodeFromHandle(item.hItem), false, TreeViewAction.Expand); OnBeforeExpand(e); } else { e = new TreeViewCancelEventArgs(NodeFromHandle(item.hItem), false, TreeViewAction.Collapse); OnBeforeCollapse(e); } return (IntPtr)(e.Cancel? 1: 0); } private unsafe void TvnExpanded(NativeMethods.NMTREEVIEW* nmtv) { NativeMethods.TV_ITEM item = nmtv->itemNew; // Check for invalid node handle if (item.hItem == IntPtr.Zero) { return; } TreeViewEventArgs e; TreeNode node = NodeFromHandle(item.hItem); // Note that IsExpanded is invalid for the moment, so we use item item.state to branch. if ((item.state & NativeMethods.TVIS_EXPANDED) == 0) { e = new TreeViewEventArgs(node, TreeViewAction.Collapse); OnAfterCollapse(e); } else { e = new TreeViewEventArgs(node, TreeViewAction.Expand); OnAfterExpand(e); } } private unsafe IntPtr TvnSelecting(NativeMethods.NMTREEVIEW* nmtv) { if (treeViewState[ TREEVIEWSTATE_ignoreSelects]) { return (IntPtr)1; } // Check for invalid node handle if (nmtv->itemNew.hItem == IntPtr.Zero) { return IntPtr.Zero; } TreeNode node = NodeFromHandle(nmtv->itemNew.hItem); TreeViewAction action = TreeViewAction.Unknown; switch(nmtv->action) { case NativeMethods.TVC_BYKEYBOARD: action = TreeViewAction.ByKeyboard; break; case NativeMethods.TVC_BYMOUSE: action = TreeViewAction.ByMouse; break; } TreeViewCancelEventArgs e = new TreeViewCancelEventArgs(node, false, action); OnBeforeSelect(e); return (IntPtr)(e.Cancel? 1: 0); } private unsafe void TvnSelected(NativeMethods.NMTREEVIEW* nmtv) { if (nodesCollectionClear) //if called thru the Clear( ) of treeNodeCollection then just return... { return; } if (nmtv->itemNew.hItem != IntPtr.Zero) { TreeViewAction action = TreeViewAction.Unknown; switch(nmtv->action) { case NativeMethods.TVC_BYKEYBOARD: action = TreeViewAction.ByKeyboard; break; case NativeMethods.TVC_BYMOUSE: action = TreeViewAction.ByMouse; break; } OnAfterSelect(new TreeViewEventArgs(NodeFromHandle(nmtv->itemNew.hItem), action)); } // TreeView doesn't properly revert back to the unselected image // if the unselected image is blank. // NativeMethods.RECT rc = new NativeMethods.RECT(); *((IntPtr *) &rc.left) = nmtv->itemOld.hItem; if (nmtv->itemOld.hItem != IntPtr.Zero) { if (unchecked( (int) (long)UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TVM_GETITEMRECT, 1, ref rc)) != 0) SafeNativeMethods.InvalidateRect(new HandleRef(this, Handle), ref rc, true); } } private IntPtr TvnBeginLabelEdit(NativeMethods.NMTVDISPINFO nmtvdi) { // Check for invalid node handle if (nmtvdi.item.hItem == IntPtr.Zero) { return IntPtr.Zero; } TreeNode editingNode = NodeFromHandle(nmtvdi.item.hItem); NodeLabelEditEventArgs e = new NodeLabelEditEventArgs(editingNode); OnBeforeLabelEdit(e); if (!e.CancelEdit) editNode = editingNode; return (IntPtr)(e.CancelEdit ? 1 : 0); } private IntPtr TvnEndLabelEdit(NativeMethods.NMTVDISPINFO nmtvdi) { editNode = null; // Check for invalid node handle if (nmtvdi.item.hItem == IntPtr.Zero) { return (IntPtr)1; } TreeNode node = NodeFromHandle(nmtvdi.item.hItem); string newText = (nmtvdi.item.pszText == IntPtr.Zero ? null : Marshal.PtrToStringAuto(nmtvdi.item.pszText)); NodeLabelEditEventArgs e = new NodeLabelEditEventArgs(node, newText); OnAfterLabelEdit(e); if (newText != null && !e.CancelEdit && node != null) { node.text = newText; if (Scrollable) ForceScrollbarUpdate(true); } return (IntPtr)(e.CancelEdit ? 0 : 1); } internal override void UpdateStylesCore() { base.UpdateStylesCore(); if (IsHandleCreated && CheckBoxes) { if (StateImageList != null) { // Setting the TVS_CHECKBOXES window style also causes the TreeView to display the default checkbox // images rather than the user specified StateImageList. We send a TVM_SETIMAGELIST to restore the // user's images. if (internalStateImageList != null) { SendMessage(NativeMethods.TVM_SETIMAGELIST, NativeMethods.TVSIL_STATE, internalStateImageList.Handle); } } } } // Setting the NativeMethods.TVS_CHECKBOXES style clears the checked state private void UpdateCheckedState(TreeNode node, bool update) { // This looks funny, but CheckedInternal returns the cached isChecked value and the internal // setter will blindly issue TVM_SETITEM so this gets us back in [....]. if (update) { node.CheckedInternal = node.CheckedInternal; for (int i = node.Nodes.Count - 1; i >= 0; i--) { UpdateCheckedState(node.Nodes[i], update); } } else { node.CheckedInternal = false; for (int i = node.Nodes.Count - 1; i >= 0; i--) { UpdateCheckedState(node.Nodes[i], update); } } } private void WmMouseDown(ref Message m, MouseButtons button, int clicks) { // Required to put the TreeView in sane-state for painting proper highlighting of selectedNodes. // If the user shows the ContextMenu bu overiding the WndProc( ), then the treeview // goes into the weird state where the high-light gets locked to the node on which the ContextMenu was shown. // So we need to get the native TREEIVEW out of this weird state. // Refer to VSWhidbey : 249191. SendMessage(NativeMethods.TVM_SELECTITEM, NativeMethods.TVGN_DROPHILITE, null); // Windows TreeView pushes its own message loop in WM_xBUTTONDOWN, so fire the // event before calling defWndProc or else it won't get fired until the button // comes back up. OnMouseDown(new MouseEventArgs(button, clicks, NativeMethods.Util.SignedLOWORD(m.LParam), NativeMethods.Util.SignedHIWORD(m.LParam), 0)); //If Validation is cancelled dont fire any events through the Windows TreeView's message loop... if (!ValidationCancelled) { DefWndProc(ref m); } } /// /// /// Performs custom draw handling /// ///private void CustomDraw(ref Message m) { NativeMethods.NMTVCUSTOMDRAW nmcd = (NativeMethods.NMTVCUSTOMDRAW)m.GetLParam(typeof(NativeMethods.NMTVCUSTOMDRAW)); // Find out which stage we're drawing switch (nmcd.nmcd.dwDrawStage) { // Do we want OwnerDraw for this paint cycle? case NativeMethods.CDDS_PREPAINT: m.Result = (IntPtr)NativeMethods.CDRF_NOTIFYITEMDRAW; // yes, we do... return; // We've got opt-in on owner draw for items - so handle each one. case NativeMethods.CDDS_ITEMPREPAINT: // get the node Debug.Assert(nmcd.nmcd.dwItemSpec != IntPtr.Zero, "Invalid node handle in ITEMPREPAINT"); TreeNode node = NodeFromHandle((IntPtr)nmcd.nmcd.dwItemSpec); if (node == null) { // this can happen if we are presently inserting the node - it hasn't yet // been added to the handle table m.Result = (IntPtr)(NativeMethods.CDRF_SKIPDEFAULT); return; } int state = nmcd.nmcd.uItemState; // The commctrl TreeView allows you to draw the whole row of a node // or nothing at all. The way we provide OwnerDrawText is by asking it // to draw everything but the text - to do this, we set text color same // as background color. if (drawMode == TreeViewDrawMode.OwnerDrawText) { nmcd.clrText = nmcd.clrTextBk; Marshal.StructureToPtr(nmcd, m.LParam, false); m.Result = (IntPtr) (NativeMethods.CDRF_NEWFONT | NativeMethods.CDRF_NOTIFYPOSTPAINT); return; } else if (drawMode == TreeViewDrawMode.OwnerDrawAll) { Graphics g = Graphics.FromHdcInternal(nmcd.nmcd.hdc); DrawTreeNodeEventArgs e; try { Rectangle bounds = node.RowBounds; NativeMethods.SCROLLINFO si = new NativeMethods.SCROLLINFO(); si.cbSize = Marshal.SizeOf(typeof(NativeMethods.SCROLLINFO)); si.fMask = NativeMethods.SIF_POS; if (UnsafeNativeMethods.GetScrollInfo(new HandleRef(this, Handle), NativeMethods.SB_HORZ,si) != false) { // VsW : 432718 // need to get the correct bounds if horizontal scroll bar is shown. // In this case the bounds.X needs to be negative and width needs to be updated to the increased width (scrolled region). int value = si.nPos; if (value > 0) { bounds.X -= value; bounds.Width += value; } } e = new DrawTreeNodeEventArgs(g, node, bounds, (TreeNodeStates) (state)); OnDrawNode(e); } finally { g.Dispose(); } if (!e.DrawDefault) { m.Result = (IntPtr)(NativeMethods.CDRF_SKIPDEFAULT); return; } } //TreeViewDrawMode.Normal case #if DEBUGGING // Diagnostic output Debug.WriteLine("Itemstate: "+state); Debug.WriteLine("Itemstate: "+ "\nDISABLED" + (((state & NativeMethods.CDIS_DISABLED) != 0) ? "TRUE" : "FALSE") + "\nHOT" + (((state & NativeMethods.CDIS_HOT) != 0) ? "TRUE" : "FALSE") + "\nGRAYED" + (((state & NativeMethods.CDIS_GRAYED) != 0) ? "TRUE" : "FALSE") + "\nSELECTED" + (((state & NativeMethods.CDIS_SELECTED) != 0) ? "TRUE" : "FALSE") + "\nFOCUS" + (((state & NativeMethods.CDIS_FOCUS) != 0) ? "TRUE" : "FALSE") + "\nDEFAULT" + (((state & NativeMethods.CDIS_DEFAULT) != 0) ? "TRUE" : "FALSE") + "\nMARKED" + (((state & NativeMethods.CDIS_MARKED) != 0) ? "TRUE" : "FALSE") + "\nINDETERMINATE" + (((state & NativeMethods.CDIS_INDETERMINATE) != 0) ? "TRUE" : "FALSE")); #endif OwnerDrawPropertyBag renderinfo = GetItemRenderStyles(node,state); // TreeView has problems with drawing items at times; it gets confused // as to which colors apply to which items (see focus rectangle shifting; // when one item is selected, click and hold on another). This needs to be fixed. bool colordelta = false; Color riFore = renderinfo.ForeColor; Color riBack = renderinfo.BackColor; if (renderinfo != null && !riFore.IsEmpty) { nmcd.clrText = ColorTranslator.ToWin32(riFore); colordelta = true; } if (renderinfo != null && !riBack.IsEmpty) { nmcd.clrTextBk = ColorTranslator.ToWin32(riBack); colordelta = true; } if (colordelta) { Marshal.StructureToPtr(nmcd, m.LParam, false); } if (renderinfo != null && renderinfo.Font != null) { // Mess with the DC directly... SafeNativeMethods.SelectObject(new HandleRef(nmcd.nmcd, nmcd.nmcd.hdc), new HandleRef(renderinfo, renderinfo.FontHandle)); // There is a problem in winctl that clips node fonts if the fontsize // is larger than the treeview font size. The behavior is much better in comctl 5 and above. m.Result = (IntPtr)NativeMethods.CDRF_NEWFONT; return; } // fall through and do the default drawing work goto default; case (NativeMethods.CDDS_ITEMPOSTPAINT): //User draws only the text in OwnerDrawText mode, as explained in comments above if (drawMode == TreeViewDrawMode.OwnerDrawText) { Debug.Assert(nmcd.nmcd.dwItemSpec != IntPtr.Zero, "Invalid node handle in ITEMPOSTPAINT"); // Get the node node = NodeFromHandle((IntPtr)nmcd.nmcd.dwItemSpec); if (node == null) { // this can happen if we are presently inserting the node - it hasn't yet // been added to the handle table return; } Graphics g = Graphics.FromHdcInternal(nmcd.nmcd.hdc); DrawTreeNodeEventArgs e; try { Rectangle bounds = node.Bounds; Size textSize = TextRenderer.MeasureText(node.Text, node.TreeView.Font); Point textLoc = new Point(bounds.X -1, bounds.Y); // required to center the text bounds = new Rectangle(textLoc, new Size(textSize.Width, bounds.Height)); e = new DrawTreeNodeEventArgs(g, node, bounds, (TreeNodeStates) (nmcd.nmcd.uItemState)); OnDrawNode(e); if (e.DrawDefault) { //Simulate default text drawing here TreeNodeStates curState = e.State; Font font = (node.NodeFont != null) ? node.NodeFont : node.TreeView.Font; Color color = (((curState & TreeNodeStates.Selected) == TreeNodeStates.Selected) && node.TreeView.Focused) ? SystemColors.HighlightText : (node.ForeColor != Color.Empty) ? node.ForeColor : node.TreeView.ForeColor; // Draw the actual node. if ((curState & TreeNodeStates.Selected) == TreeNodeStates.Selected) { g.FillRectangle(SystemBrushes.Highlight, bounds); ControlPaint.DrawFocusRectangle(g, bounds, color, SystemColors.Highlight); TextRenderer.DrawText(g, e.Node.Text, font, bounds, color, TextFormatFlags.Default); } else { // For Dev10 bug 478621: We need to draw the BackColor of // nodes same as BackColor of treeview. using (Brush brush = new SolidBrush(BackColor)) { g.FillRectangle(brush, bounds); } TextRenderer.DrawText(g, e.Node.Text, font, bounds, color, TextFormatFlags.Default); } } } finally { g.Dispose(); } m.Result = (IntPtr)NativeMethods.CDRF_NOTIFYSUBITEMDRAW; return; } goto default; default: // just in case we get a spurious message, tell it to do the right thing m.Result = (IntPtr)NativeMethods.CDRF_DODEFAULT; return; } } /// /// /// Generates colors for each item. This can be overridden to provide colors on a per state/per node /// basis, rather than using the ForeColor/BackColor/NodeFont properties on TreeNode. /// /// ///protected OwnerDrawPropertyBag GetItemRenderStyles(TreeNode node, int state) { OwnerDrawPropertyBag retval = new OwnerDrawPropertyBag(); if (node == null || node.propBag == null) return retval; // we only change colors if we're displaying things normally if ((state & (NativeMethods.CDIS_SELECTED | NativeMethods.CDIS_GRAYED | NativeMethods.CDIS_HOT | NativeMethods.CDIS_DISABLED))==0) { retval.ForeColor = node.propBag.ForeColor; retval.BackColor = node.propBag.BackColor; } retval.Font = node.propBag.Font; return retval; } private unsafe bool WmShowToolTip(ref Message m) { NativeMethods.NMHDR* nmhdr = (NativeMethods.NMHDR*)m.LParam; IntPtr tooltipHandle = nmhdr->hwndFrom; NativeMethods.TV_HITTESTINFO tvhip = new NativeMethods.TV_HITTESTINFO(); Point pos = Cursor.Position; pos = PointToClientInternal(pos); tvhip.pt_x = pos.X; tvhip.pt_y = pos.Y; IntPtr hnode = UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TVM_HITTEST, 0, tvhip); if (hnode != IntPtr.Zero && ((tvhip.flags & NativeMethods.TVHT_ONITEM) != 0)) { TreeNode tn = NodeFromHandle(hnode); if (tn != null) { if (!ShowNodeToolTips) // default ToolTips { Rectangle bounds = tn.Bounds; bounds.Location = this.PointToScreen(bounds.Location); UnsafeNativeMethods.SendMessage(new HandleRef(this, tooltipHandle), NativeMethods.TTM_ADJUSTRECT, 1, ref bounds); SafeNativeMethods.SetWindowPos(new HandleRef(this, tooltipHandle), NativeMethods.HWND_TOPMOST, bounds.Left, bounds.Top, 0, 0, NativeMethods.SWP_NOACTIVATE | NativeMethods.SWP_NOSIZE | NativeMethods.SWP_NOZORDER); return true; } } } return false; } /// /// /// ///private void WmNeedText(ref Message m) { NativeMethods.TOOLTIPTEXT ttt = (NativeMethods.TOOLTIPTEXT) m.GetLParam(typeof(NativeMethods.TOOLTIPTEXT)); string tipText = controlToolTipText; NativeMethods.TV_HITTESTINFO tvhip = new NativeMethods.TV_HITTESTINFO(); Point pos = Cursor.Position; pos = PointToClientInternal(pos); tvhip.pt_x = pos.X; tvhip.pt_y = pos.Y; IntPtr hnode = UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TVM_HITTEST, 0, tvhip); if (hnode != IntPtr.Zero && ((tvhip.flags & NativeMethods.TVHT_ONITEM) != 0)) { TreeNode tn = NodeFromHandle(hnode); if (ShowNodeToolTips && tn != null && (!string.IsNullOrEmpty(tn.ToolTipText))) { tipText = tn.ToolTipText; } else if (tn != null && tn.Bounds.Right > this.Bounds.Right) { tipText = tn.Text; } else { tipText = null; } } ttt.lpszText = tipText; ttt.hinst = IntPtr.Zero; // RightToLeft reading order // if (RightToLeft == RightToLeft.Yes) { ttt.uFlags |= NativeMethods.TTF_RTLREADING; } Marshal.StructureToPtr(ttt, m.LParam, false); } private unsafe void WmNotify(ref Message m) { NativeMethods.NMHDR* nmhdr = (NativeMethods.NMHDR *)m.LParam; // Custom draw code is handled separately. // if ((nmhdr->code == NativeMethods.NM_CUSTOMDRAW)) { CustomDraw(ref m); } else { NativeMethods.NMTREEVIEW* nmtv = (NativeMethods.NMTREEVIEW*)m.LParam; switch (nmtv->nmhdr.code) { case NativeMethods.TVN_ITEMEXPANDINGA: case NativeMethods.TVN_ITEMEXPANDINGW: m.Result = TvnExpanding(nmtv); break; case NativeMethods.TVN_ITEMEXPANDEDA: case NativeMethods.TVN_ITEMEXPANDEDW: TvnExpanded(nmtv); break; case NativeMethods.TVN_SELCHANGINGA: case NativeMethods.TVN_SELCHANGINGW: m.Result = TvnSelecting(nmtv); break; case NativeMethods.TVN_SELCHANGEDA: case NativeMethods.TVN_SELCHANGEDW: TvnSelected(nmtv); break; case NativeMethods.TVN_BEGINDRAGA: case NativeMethods.TVN_BEGINDRAGW: TvnBeginDrag(MouseButtons.Left, nmtv); break; case NativeMethods.TVN_BEGINRDRAGA: case NativeMethods.TVN_BEGINRDRAGW: TvnBeginDrag(MouseButtons.Right, nmtv); break; case NativeMethods.TVN_BEGINLABELEDITA: case NativeMethods.TVN_BEGINLABELEDITW: m.Result = TvnBeginLabelEdit((NativeMethods.NMTVDISPINFO)m.GetLParam(typeof(NativeMethods.NMTVDISPINFO))); break; case NativeMethods.TVN_ENDLABELEDITA: case NativeMethods.TVN_ENDLABELEDITW: m.Result = TvnEndLabelEdit((NativeMethods.NMTVDISPINFO)m.GetLParam(typeof(NativeMethods.NMTVDISPINFO))); break; case NativeMethods.NM_CLICK: case NativeMethods.NM_RCLICK: MouseButtons button = MouseButtons.Left; NativeMethods.TV_HITTESTINFO tvhip = new NativeMethods.TV_HITTESTINFO(); Point pos = Cursor.Position; pos = PointToClientInternal(pos); tvhip.pt_x = pos.X; tvhip.pt_y = pos.Y; IntPtr hnode = UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TVM_HITTEST, 0, tvhip); if (nmtv->nmhdr.code != NativeMethods.NM_CLICK || (tvhip.flags & NativeMethods.TVHT_ONITEM) != 0) { button = nmtv->nmhdr.code == NativeMethods.NM_CLICK ? MouseButtons.Left : MouseButtons.Right; } // The treeview's WndProc doesn't get the WM_LBUTTONUP messages when // LBUTTONUP happens on TVHT_ONITEM. This is a comctl quirk. // We work around that by calling OnMouseUp here. if (nmtv->nmhdr.code != NativeMethods.NM_CLICK || (tvhip.flags & NativeMethods.TVHT_ONITEM) != 0 || FullRowSelect) { if (hnode != IntPtr.Zero && !ValidationCancelled) { OnNodeMouseClick(new TreeNodeMouseClickEventArgs(NodeFromHandle(hnode), button, 1, pos.X, pos.Y)); OnClick(new MouseEventArgs(button, 1, pos.X, pos.Y, 0)); OnMouseClick(new MouseEventArgs(button, 1, pos.X, pos.Y, 0)); } } if (nmtv->nmhdr.code == NativeMethods.NM_RCLICK) { TreeNode treeNode = NodeFromHandle(hnode); if (treeNode != null && (treeNode.ContextMenu != null || treeNode.ContextMenuStrip != null)) { ShowContextMenu(treeNode); } else { treeViewState[TREEVIEWSTATE_showTreeViewContextMenu] = true; SendMessage(NativeMethods.WM_CONTEXTMENU, Handle, SafeNativeMethods.GetMessagePos()); } m.Result = (IntPtr)1; } if (!treeViewState[TREEVIEWSTATE_mouseUpFired]) { if (nmtv->nmhdr.code != NativeMethods.NM_CLICK || (tvhip.flags & NativeMethods.TVHT_ONITEM) != 0) { // The treeview's WndProc doesn't get the WM_LBUTTONUP messages when // LBUTTONUP happens on TVHT_ONITEM. This is a comctl quirk. // We work around that by calling OnMouseUp here. OnMouseUp(new MouseEventArgs(button, 1, pos.X, pos.Y, 0)); treeViewState[TREEVIEWSTATE_mouseUpFired] = true; } } break; } } } /// /// /// Shows the context menu for the Treenode. /// ///private void ShowContextMenu(TreeNode treeNode) { if (treeNode.ContextMenu != null || treeNode.ContextMenuStrip != null) { ContextMenu contextMenu = treeNode.ContextMenu; ContextMenuStrip menu = treeNode.ContextMenuStrip; if (contextMenu != null) { NativeMethods.POINT pt = new NativeMethods.POINT(); UnsafeNativeMethods.GetCursorPos(pt); // VS7 #38994 // The solution to this problem was found in MSDN Article ID: Q135788. // Summary: the current window must be made the foreground window // before calling TrackPopupMenuEx, and a task switch must be // forced after the call. UnsafeNativeMethods.SetForegroundWindow(new HandleRef(this, this.Handle)); contextMenu.OnPopup( EventArgs.Empty ); SafeNativeMethods.TrackPopupMenuEx(new HandleRef(contextMenu, contextMenu.Handle), NativeMethods.TPM_VERTICAL, pt.x, pt.y, new HandleRef(this, this.Handle), null); // Force task switch (see above) UnsafeNativeMethods.PostMessage(new HandleRef(this, this.Handle), NativeMethods.WM_NULL, IntPtr.Zero, IntPtr.Zero); } // VsWhidbey : 432712. // Need to send TVM_SELECTITEM to highlight the node while the contextMenuStrip is being shown. else if (menu != null) { UnsafeNativeMethods.PostMessage(new HandleRef(this, Handle), NativeMethods.TVM_SELECTITEM, NativeMethods.TVGN_DROPHILITE, treeNode.Handle); menu.ShowInternal(this, PointToClient(MousePosition),/*keyboardActivated*/false); menu.Closing += new ToolStripDropDownClosingEventHandler(this.ContextMenuStripClosing); } } } // VsWhidbey : 432712. // Need to send TVM_SELECTITEM to reset the node-highlighting while the contextMenuStrip is being closed so that the treeview reselects the SelectedNode. private void ContextMenuStripClosing(object sender, ToolStripDropDownClosingEventArgs e) { ContextMenuStrip strip = sender as ContextMenuStrip; // Unhook the Event. strip.Closing -= new ToolStripDropDownClosingEventHandler(this.ContextMenuStripClosing); SendMessage(NativeMethods.TVM_SELECTITEM, NativeMethods.TVGN_DROPHILITE, null); } private void WmPrint(ref Message m) { base.WndProc(ref m); if ((NativeMethods.PRF_NONCLIENT & (int)m.LParam) != 0 && Application.RenderWithVisualStyles && this.BorderStyle == BorderStyle.Fixed3D) { IntSecurity.UnmanagedCode.Assert(); try { using (Graphics g = Graphics.FromHdc(m.WParam)) { Rectangle rect = new Rectangle(0, 0, this.Size.Width - 1, this.Size.Height - 1); g.DrawRectangle(new Pen(VisualStyleInformation.TextControlBorder), rect); rect.Inflate(-1, -1); g.DrawRectangle(SystemPens.Window, rect); } } finally { CodeAccessPermission.RevertAssert(); } } } /// /// /// ///[SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] protected override void WndProc(ref Message m) { switch (m.Msg) { case NativeMethods.WM_WINDOWPOSCHANGING: case NativeMethods.WM_NCCALCSIZE: case NativeMethods.WM_WINDOWPOSCHANGED: case NativeMethods.WM_SIZE: // While we are changing size of treeView to avoid the scrollbar; dont respond to the window-sizing messages. if (treeViewState[TREEVIEWSTATE_stopResizeWindowMsgs]) { //Debug.WriteLineIf(treeViewState[TREEVIEWSTATE_stopResizeWindowMsgs], "Sending message directly to DefWndProc() : " + m.ToString()); DefWndProc(ref m); } else { base.WndProc(ref m); } break; case NativeMethods.WM_HSCROLL: base.WndProc(ref m); if (DrawMode == TreeViewDrawMode.OwnerDrawAll) { //VsW : 432718 Invalidate(); } break; case NativeMethods.WM_PRINT: WmPrint(ref m); break; case NativeMethods.TVM_SETITEMA: case NativeMethods.TVM_SETITEMW: base.WndProc(ref m); if (this.CheckBoxes) { NativeMethods.TV_ITEM item = (NativeMethods.TV_ITEM) m.GetLParam(typeof(NativeMethods.TV_ITEM)); // Check for invalid node handle if (item.hItem != IntPtr.Zero) { NativeMethods.TV_ITEM item1 = new NativeMethods.TV_ITEM(); item1.mask = NativeMethods.TVIF_HANDLE | NativeMethods.TVIF_STATE; item1.hItem = item.hItem; item1.stateMask = NativeMethods.TVIS_STATEIMAGEMASK; UnsafeNativeMethods.SendMessage(new HandleRef(null, this.Handle), NativeMethods.TVM_GETITEM, 0, ref item1); TreeNode node = NodeFromHandle(item.hItem); node.CheckedStateInternal = ((item1.state >> 12) > 1); } } break; case NativeMethods.WM_NOTIFY: NativeMethods.NMHDR nmhdr = (NativeMethods.NMHDR) m.GetLParam(typeof(NativeMethods.NMHDR)); switch (nmhdr.code) { case NativeMethods.TTN_GETDISPINFOA: case NativeMethods.TTN_GETDISPINFOW: // MSDN: // Setting the max width has the added benefit of enabling multiline // tool tips! // UnsafeNativeMethods.SendMessage(new HandleRef(nmhdr, nmhdr.hwndFrom), NativeMethods.TTM_SETMAXTIPWIDTH, 0, SystemInformation.MaxWindowTrackSize.Width); WmNeedText(ref m); m.Result = (IntPtr)1; return; case NativeMethods.TTN_SHOW: if (WmShowToolTip(ref m)) { m.Result = (IntPtr)1; return; } else { base.WndProc(ref m); break; } default: base.WndProc(ref m); break; } break; case NativeMethods.WM_REFLECT + NativeMethods.WM_NOTIFY: WmNotify(ref m); break; case NativeMethods.WM_LBUTTONDBLCLK: WmMouseDown(ref m, MouseButtons.Left, 2); //just maintain state and fire double click.. in final mouseUp... treeViewState[TREEVIEWSTATE_doubleclickFired] = true; //fire Up in the Wndproc !! treeViewState[TREEVIEWSTATE_mouseUpFired] = false; //problem getting the UP... outside the control... // CaptureInternal = true; break; case NativeMethods.WM_LBUTTONDOWN: try { treeViewState[TREEVIEWSTATE_ignoreSelects] = true; FocusInternal(); } finally { treeViewState[ TREEVIEWSTATE_ignoreSelects] = false; } //Always Reset the MouseupFired.... treeViewState[TREEVIEWSTATE_mouseUpFired] = false; NativeMethods.TV_HITTESTINFO tvhip = new NativeMethods.TV_HITTESTINFO(); tvhip.pt_x = NativeMethods.Util.SignedLOWORD(m.LParam); tvhip.pt_y = NativeMethods.Util.SignedHIWORD(m.LParam); hNodeMouseDown = UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TVM_HITTEST, 0, tvhip); // This gets around the TreeView behavior of temporarily moving the selection // highlight to a node when the user clicks on its checkbox. if ((tvhip.flags & NativeMethods.TVHT_ONITEMSTATEICON) != 0) { //We donot pass the Message to the Control .. so fire MouseDowm ... OnMouseDown(new MouseEventArgs(MouseButtons.Left, 1, NativeMethods.Util.SignedLOWORD(m.LParam), NativeMethods.Util.SignedHIWORD(m.LParam), 0)); if (!ValidationCancelled && CheckBoxes) { TreeNode node = NodeFromHandle(hNodeMouseDown); bool eventReturn = TreeViewBeforeCheck(node, TreeViewAction.ByMouse); if (!eventReturn && node != null) { node.CheckedInternal = !node.CheckedInternal; TreeViewAfterCheck(node, TreeViewAction.ByMouse); } } m.Result = IntPtr.Zero; } else { WmMouseDown(ref m, MouseButtons.Left, 1); } downButton = MouseButtons.Left; break; case NativeMethods.WM_LBUTTONUP: case NativeMethods.WM_RBUTTONUP: NativeMethods.TV_HITTESTINFO tvhi = new NativeMethods.TV_HITTESTINFO(); tvhi.pt_x = NativeMethods.Util.SignedLOWORD(m.LParam); tvhi.pt_y = NativeMethods.Util.SignedHIWORD(m.LParam); IntPtr hnode = UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TVM_HITTEST, 0, tvhi); //Important for CheckBoxes ... click needs to be fired ... // if(hnode != IntPtr.Zero) { if (!ValidationCancelled && !treeViewState[TREEVIEWSTATE_doubleclickFired] & !treeViewState[TREEVIEWSTATE_mouseUpFired]) { //OnClick(EventArgs.Empty); //If the hit-tested node here is the same as the node we hit-tested //on mouse down then we will fire our OnNodeMoseClick event. if (hnode == hNodeMouseDown) { OnNodeMouseClick(new TreeNodeMouseClickEventArgs(NodeFromHandle(hnode), downButton, 1, NativeMethods.Util.SignedLOWORD(m.LParam), NativeMethods.Util.SignedHIWORD(m.LParam))); } OnClick(new MouseEventArgs(downButton, 1, NativeMethods.Util.SignedLOWORD(m.LParam), NativeMethods.Util.SignedHIWORD(m.LParam), 0)); OnMouseClick(new MouseEventArgs(downButton, 1, NativeMethods.Util.SignedLOWORD(m.LParam), NativeMethods.Util.SignedHIWORD(m.LParam), 0)); } if (treeViewState[TREEVIEWSTATE_doubleclickFired]) { treeViewState[TREEVIEWSTATE_doubleclickFired] = false; if (!ValidationCancelled) { //OnDoubleClick(EventArgs.Empty); OnNodeMouseDoubleClick(new TreeNodeMouseClickEventArgs(NodeFromHandle(hnode), downButton, 2, NativeMethods.Util.SignedLOWORD(m.LParam), NativeMethods.Util.SignedHIWORD(m.LParam))); OnDoubleClick(new MouseEventArgs(downButton, 2, NativeMethods.Util.SignedLOWORD(m.LParam), NativeMethods.Util.SignedHIWORD(m.LParam), 0)); OnMouseDoubleClick(new MouseEventArgs(downButton, 2, NativeMethods.Util.SignedLOWORD(m.LParam), NativeMethods.Util.SignedHIWORD(m.LParam), 0)); } } } if (!treeViewState[TREEVIEWSTATE_mouseUpFired]) OnMouseUp(new MouseEventArgs(downButton, 1, NativeMethods.Util.SignedLOWORD(m.LParam), NativeMethods.Util.SignedHIWORD(m.LParam), 0)); treeViewState[TREEVIEWSTATE_doubleclickFired] = false; treeViewState[TREEVIEWSTATE_mouseUpFired] = false; CaptureInternal = false; //always clear our hit-tested node we cached on mouse down hNodeMouseDown = IntPtr.Zero; break; case NativeMethods.WM_MBUTTONDBLCLK: //fire Up in the Wndproc !! treeViewState[TREEVIEWSTATE_mouseUpFired] = false; WmMouseDown(ref m, MouseButtons.Middle, 2); break; case NativeMethods.WM_MBUTTONDOWN: //Always Reset the MouseupFired.... treeViewState[TREEVIEWSTATE_mouseUpFired] = false; WmMouseDown(ref m, MouseButtons.Middle, 1); downButton = MouseButtons.Middle; break; case NativeMethods.WM_MOUSELEAVE: // if the mouse leaves and then reenters the TreeView // NodeHovered events should be raised. prevHoveredNode = null; base.WndProc(ref m); break; case NativeMethods.WM_RBUTTONDBLCLK: WmMouseDown(ref m, MouseButtons.Right, 2); //just maintain state and fire double click.. in final mouseUp... treeViewState[TREEVIEWSTATE_doubleclickFired] = true; //fire Up in the Wndproc !! treeViewState[TREEVIEWSTATE_mouseUpFired] = false; //problem getting the UP... outside the control... // CaptureInternal = true; break; case NativeMethods.WM_RBUTTONDOWN: //Always Reset the MouseupFired.... treeViewState[TREEVIEWSTATE_mouseUpFired] = false; //Cache the hit-tested node for verification when mouse up is fired NativeMethods.TV_HITTESTINFO tvhit = new NativeMethods.TV_HITTESTINFO(); tvhit.pt_x = NativeMethods.Util.SignedLOWORD(m.LParam); tvhit.pt_y = NativeMethods.Util.SignedHIWORD(m.LParam); hNodeMouseDown = UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TVM_HITTEST, 0, tvhit); WmMouseDown(ref m, MouseButtons.Right, 1); downButton = MouseButtons.Right; break; //# VS7 15052 case NativeMethods.WM_SYSCOLORCHANGE: SendMessage(NativeMethods.TVM_SETINDENT, Indent, 0); base.WndProc(ref m); break; case NativeMethods.WM_SETFOCUS: // If we get focus through the LBUttonDown .. we might have done the validation... // so skip it.. if (treeViewState[TREEVIEWSTATE_lastControlValidated]) { treeViewState[TREEVIEWSTATE_lastControlValidated] = false; WmImeSetFocus(); DefWndProc(ref m); OnGotFocus(EventArgs.Empty); } else { base.WndProc(ref m); } break; case NativeMethods.WM_CONTEXTMENU: if (treeViewState[TREEVIEWSTATE_showTreeViewContextMenu]) { treeViewState[TREEVIEWSTATE_showTreeViewContextMenu] = false; base.WndProc(ref m); } else { // this is the Shift + F10 Case.... TreeNode treeNode = SelectedNode; if (treeNode != null && (treeNode.ContextMenu != null || treeNode.ContextMenuStrip !=null)) { Point client; client = new Point(treeNode.Bounds.X , treeNode.Bounds.Y + treeNode.Bounds.Height / 2); // VisualStudio7 # 156, only show the context menu when clicked in the client area if (ClientRectangle.Contains( client )) { if (treeNode.ContextMenu != null) { treeNode.ContextMenu.Show(this, client); } else if (treeNode.ContextMenuStrip !=null) { bool keyboardActivated = (unchecked((int)(long)m.LParam) == -1); treeNode.ContextMenuStrip.ShowInternal(this, client, keyboardActivated); } } } else { // in this case we dont have a selected node. The base // will ensure we're constrained to the client area. base.WndProc (ref m); } } break; default: base.WndProc(ref m); break; } } } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. //------------------------------------------------------------------------------ // // Copyright (c) Microsoft Corporation. All rights reserved. // //----------------------------------------------------------------------------- /* */ namespace System.Windows.Forms { using System.Runtime.Remoting; using System.Runtime.InteropServices; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System; using System.Security.Permissions; using System.Security; using System.Drawing; using System.Windows.Forms.Internal; using System.Drawing.Design; using System.ComponentModel; using System.ComponentModel.Design; using System.Collections; using Microsoft.Win32; using System.Reflection; using System.Windows.Forms.Layout; using System.Globalization; using System.Windows.Forms.VisualStyles; ////// /// [ ComVisible(true), ClassInterface(ClassInterfaceType.AutoDispatch), DefaultProperty("Nodes"), DefaultEvent("AfterSelect"), Docking(DockingBehavior.Ask), Designer("System.Windows.Forms.Design.TreeViewDesigner, " + AssemblyRef.SystemDesign), SRDescription(SR.DescriptionTreeView) ] public class TreeView : Control { private static readonly int MaxIndent = 32000; // Maximum allowable TreeView indent private static readonly string backSlash = "\\"; private const int DefaultTreeViewIndent = 19; private DrawTreeNodeEventHandler onDrawNode; private NodeLabelEditEventHandler onBeforeLabelEdit; private NodeLabelEditEventHandler onAfterLabelEdit; private TreeViewCancelEventHandler onBeforeCheck; private TreeViewEventHandler onAfterCheck; private TreeViewCancelEventHandler onBeforeCollapse; private TreeViewEventHandler onAfterCollapse; private TreeViewCancelEventHandler onBeforeExpand; private TreeViewEventHandler onAfterExpand; private TreeViewCancelEventHandler onBeforeSelect; private TreeViewEventHandler onAfterSelect; private ItemDragEventHandler onItemDrag; private TreeNodeMouseHoverEventHandler onNodeMouseHover; private EventHandler onRightToLeftLayoutChanged; internal TreeNode selectedNode = null; private ImageList.Indexer imageIndexer; private ImageList.Indexer selectedImageIndexer; private bool setOddHeight = false; private TreeNode prevHoveredNode = null; private bool hoveredAlready = false; private bool rightToLeftLayout = false; [SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources")] private IntPtr hNodeMouseDown = IntPtr.Zero;//ensures we fire nodeclick on the correct node private const int TREEVIEWSTATE_hideSelection = 0x00000001; private const int TREEVIEWSTATE_labelEdit = 0x00000002; private const int TREEVIEWSTATE_scrollable = 0x00000004; private const int TREEVIEWSTATE_checkBoxes = 0x00000008; private const int TREEVIEWSTATE_showLines = 0x00000010; private const int TREEVIEWSTATE_showPlusMinus = 0x00000020; private const int TREEVIEWSTATE_showRootLines = 0x00000040; private const int TREEVIEWSTATE_sorted = 0x00000080; private const int TREEVIEWSTATE_hotTracking = 0x00000100; private const int TREEVIEWSTATE_fullRowSelect = 0x00000200; private const int TREEVIEWSTATE_showNodeToolTips = 0x00000400; private const int TREEVIEWSTATE_doubleclickFired = 0x00000800; private const int TREEVIEWSTATE_mouseUpFired = 0x00001000; private const int TREEVIEWSTATE_showTreeViewContextMenu = 0x00002000; private const int TREEVIEWSTATE_lastControlValidated = 0x00004000; private const int TREEVIEWSTATE_stopResizeWindowMsgs = 0x00008000;//VSWhidbey 466949 private const int TREEVIEWSTATE_ignoreSelects = 0x00010000;//VSWhidbey 384294, 490763. // PERF: take all the bools and put them into a state variable private System.Collections.Specialized.BitVector32 treeViewState; // see TREEVIEWSTATE_ consts above ////// Displays a hierarchical list of items, or nodes. Each /// node includes a caption and an optional bitmap. The user can select a node. If /// it has sub-nodes, the user can collapse or expand the node. /// /// ////// internal ImageList.Indexer ImageIndexer { get { if (imageIndexer == null) { imageIndexer = new ImageList.Indexer(); } imageIndexer.ImageList = ImageList; return imageIndexer; } } /// /// internal ImageList.Indexer SelectedImageIndexer { get { if (selectedImageIndexer == null) { selectedImageIndexer = new ImageList.Indexer(); } selectedImageIndexer.ImageList = ImageList; return selectedImageIndexer; } } private ImageList imageList; private int indent = -1; private int itemHeight = -1; private string pathSeparator = backSlash; private BorderStyle borderStyle = System.Windows.Forms.BorderStyle.Fixed3D; internal TreeNodeCollection nodes = null; internal TreeNode editNode; internal TreeNode root; internal Hashtable nodeTable = new Hashtable(); internal bool nodesCollectionClear = false; //this is set when the treeNodeCollection is getting cleared and used by TreeView private MouseButtons downButton; private TreeViewDrawMode drawMode = TreeViewDrawMode.Normal; //Properties newly added to TreeView.... private ImageList internalStateImageList; private TreeNode topNode; private ImageList stateImageList; private Color lineColor; private string controlToolTipText = null; // Sorting private IComparer treeViewNodeSorter = null; //Events private TreeNodeMouseClickEventHandler onNodeMouseClick; private TreeNodeMouseClickEventHandler onNodeMouseDoubleClick; /// /// /// Creates a TreeView control /// public TreeView() : base() { this.treeViewState = new System.Collections.Specialized.BitVector32(TREEVIEWSTATE_showRootLines | TREEVIEWSTATE_showPlusMinus | TREEVIEWSTATE_showLines | TREEVIEWSTATE_scrollable | TREEVIEWSTATE_hideSelection); root = new TreeNode(this); // TreeView must always have an ImageIndex. SelectedImageIndexer.Index = 0; ImageIndexer.Index = 0; SetStyle(ControlStyles.UserPaint, false); SetStyle(ControlStyles.StandardClick, false); SetStyle(ControlStyles.UseTextForAccessibility, false); } ////// /// The background color for this control. Specifying null for /// this parameter sets the /// control's background color to its parent's background color. /// public override Color BackColor { get { if (ShouldSerializeBackColor()) { return base.BackColor; } else { return SystemColors.Window; } } set { base.BackColor = value; if (IsHandleCreated) { SendMessage(NativeMethods.TVM_SETBKCOLOR, 0, ColorTranslator.ToWin32(BackColor)); // This is to get around a problem in the comctl control where the lines // connecting nodes don't get the new BackColor. This messages forces // reconstruction of the line bitmaps without changing anything else. SendMessage(NativeMethods.TVM_SETINDENT, Indent, 0); } } } ////// /// ///[To be supplied.] ///[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] public override Image BackgroundImage { get { return base.BackgroundImage; } set { base.BackgroundImage = value; } } /// /// [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] new public event EventHandler BackgroundImageChanged { add { base.BackgroundImageChanged += value; } remove { base.BackgroundImageChanged -= value; } } /// /// /// ///[To be supplied.] ///[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] public override ImageLayout BackgroundImageLayout { get { return base.BackgroundImageLayout; } set { base.BackgroundImageLayout = value; } } /// /// [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] new public event EventHandler BackgroundImageLayoutChanged { add { base.BackgroundImageLayoutChanged += value; } remove { base.BackgroundImageLayoutChanged -= value; } } /// /// /// The border style of the window. /// [ SRCategory(SR.CatAppearance), DefaultValue(BorderStyle.Fixed3D), DispId(NativeMethods.ActiveX.DISPID_BORDERSTYLE), SRDescription(SR.borderStyleDescr) ] public BorderStyle BorderStyle { get { return borderStyle; } set { if (borderStyle != value) { //verify that 'value' is a valid enum type... //valid values are 0x0 to 0x2 if (!ClientUtils.IsEnumValid(value, (int)value, (int)BorderStyle.None, (int)BorderStyle.Fixed3D)) { throw new InvalidEnumArgumentException("value", (int)value, typeof(BorderStyle)); } borderStyle = value; UpdateStyles(); } } } ////// /// The value of the CheckBoxes property. The CheckBoxes /// property determines if check boxes are shown next to node in the /// tree view. /// [ SRCategory(SR.CatAppearance), DefaultValue(false), SRDescription(SR.TreeViewCheckBoxesDescr) ] public bool CheckBoxes { get { return treeViewState[TREEVIEWSTATE_checkBoxes]; } set { if (CheckBoxes != value) { treeViewState[TREEVIEWSTATE_checkBoxes] = value; if (IsHandleCreated) { // Going from true to false requires recreation if (CheckBoxes) { UpdateStyles(); } else { // Reset the Checked state after setting the checkboxes (this was Everett behavior) // The implementation of the TreeNode.Checked property has changed in Whidbey // So we need to explicit set the Checked state to false to keep the everett behavior. UpdateCheckedState(root, false); RecreateHandle(); } } } } } ////// /// ///protected override CreateParams CreateParams { [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] get { CreateParams cp = base.CreateParams; cp.ClassName = NativeMethods.WC_TREEVIEW; // V#45599 Keep the scrollbar if we are just updating styles... // if (IsHandleCreated) { int currentStyle = unchecked((int)((long)UnsafeNativeMethods.GetWindowLong(new HandleRef(this, Handle), NativeMethods.GWL_STYLE))); cp.Style |= (currentStyle & (NativeMethods.WS_HSCROLL | NativeMethods.WS_VSCROLL)); } switch (borderStyle) { case BorderStyle.Fixed3D: cp.ExStyle |= NativeMethods.WS_EX_CLIENTEDGE; break; case BorderStyle.FixedSingle: cp.Style |= NativeMethods.WS_BORDER; break; } if (!Scrollable) cp.Style |= NativeMethods.LVS_NOSCROLL; if (!HideSelection) cp.Style |= NativeMethods.TVS_SHOWSELALWAYS; if (LabelEdit) cp.Style |= NativeMethods.TVS_EDITLABELS; if (ShowLines) cp.Style |= NativeMethods.TVS_HASLINES; if (ShowPlusMinus) cp.Style |= NativeMethods.TVS_HASBUTTONS; if (ShowRootLines) cp.Style |= NativeMethods.TVS_LINESATROOT; if (HotTracking) cp.Style |= NativeMethods.TVS_TRACKSELECT; if (FullRowSelect) cp.Style |= NativeMethods.TVS_FULLROWSELECT; if (setOddHeight) { cp.Style |= NativeMethods.TVS_NONEVENHEIGHT; } // Don't set TVS_CHECKBOXES here if the window isn't created yet. // See OnHandleCreated for explanation if (ShowNodeToolTips && IsHandleCreated && !DesignMode) { cp.Style |= NativeMethods.TVS_INFOTIP; } // Don't set TVS_CHECKBOXES here if the window isn't created yet. // See OnHandleCreated for explanation if (CheckBoxes && IsHandleCreated) cp.Style |= NativeMethods.TVS_CHECKBOXES; // Don't call IsMirrored from CreateParams. That will lead to some nasty problems, since // IsMirrored ends up calling CreateParams - you dig! if (RightToLeft == RightToLeft.Yes) { if (RightToLeftLayout == true) { //We want to turn on mirroring for TreeView explicitly. cp.ExStyle |= NativeMethods.WS_EX_LAYOUTRTL; //Don't need these styles when mirroring is turned on. cp.ExStyle &= ~(NativeMethods.WS_EX_RTLREADING | NativeMethods.WS_EX_RIGHT | NativeMethods.WS_EX_LEFTSCROLLBAR); } else { cp.Style |= NativeMethods.TVS_RTLREADING; } } return cp; } } /// /// /// Deriving classes can override this to configure a default size for their control. /// This is more efficient than setting the size in the control's constructor. /// protected override Size DefaultSize { get { return new Size(121, 97); } } ////// /// This property is overridden and hidden from statement completion /// on controls that are based on Win32 Native Controls. /// [EditorBrowsable(EditorBrowsableState.Never)] protected override bool DoubleBuffered { get { return base.DoubleBuffered; } set { base.DoubleBuffered = value; } } ////// /// The current foreground color for this control, which is the /// color the control uses to draw its text. /// public override Color ForeColor { get { if (ShouldSerializeForeColor()) { return base.ForeColor; } else { return SystemColors.WindowText; } } set { base.ForeColor = value; if (IsHandleCreated) SendMessage(NativeMethods.TVM_SETTEXTCOLOR, 0, ColorTranslator.ToWin32(ForeColor)); } } ////// /// Determines whether the selection highlight spans across the width of the TreeView. /// This property will have no effect if ShowLines is true. /// [ SRCategory(SR.CatBehavior), DefaultValue(false), SRDescription(SR.TreeViewFullRowSelectDescr) ] public bool FullRowSelect { get { return treeViewState[TREEVIEWSTATE_fullRowSelect];} set { if (FullRowSelect != value) { treeViewState[TREEVIEWSTATE_fullRowSelect] = value; if (IsHandleCreated) { UpdateStyles(); } } } } ////// /// The HideSelection property specifies whether the selected node will /// be highlighted even when the TreeView loses focus. /// [ SRCategory(SR.CatBehavior), DefaultValue(true), SRDescription(SR.TreeViewHideSelectionDescr) ] public bool HideSelection { get { return treeViewState[TREEVIEWSTATE_hideSelection]; } set { if (HideSelection != value) { treeViewState[TREEVIEWSTATE_hideSelection] = value; if (IsHandleCreated) { UpdateStyles(); } } } } ////// /// The value of the HotTracking property. The HotTracking /// property determines if nodes are highlighted as the mousepointer /// passes over them. /// [ SRCategory(SR.CatBehavior), DefaultValue(false), SRDescription(SR.TreeViewHotTrackingDescr) ] public bool HotTracking { get { return treeViewState[TREEVIEWSTATE_hotTracking]; } set { if (HotTracking != value) { treeViewState[TREEVIEWSTATE_hotTracking] = value; if (IsHandleCreated) { UpdateStyles(); } } } } ////// /// The default image index for nodes in the tree view. /// [ DefaultValue(-1), SRCategory(SR.CatBehavior), Localizable(true), RefreshProperties(RefreshProperties.Repaint), TypeConverterAttribute(typeof(NoneExcludedImageIndexConverter)), Editor("System.Windows.Forms.Design.ImageIndexEditor, " + AssemblyRef.SystemDesign, typeof(UITypeEditor)), SRDescription(SR.TreeViewImageIndexDescr), RelatedImageList("ImageList") ] public int ImageIndex { get { if (imageList == null) { return -1; } if (ImageIndexer.Index >= imageList.Images.Count) { return Math.Max(0, imageList.Images.Count - 1); } return ImageIndexer.Index; } set { // If (none) is selected in the image index editor, we'll just adjust this to // mean image index 0. This is because a treeview must always have an image index - // even if no imagelist exists we want the image index to be 0. // if (value == -1) { value = 0; } if (value < 0) { throw new ArgumentOutOfRangeException("ImageIndex", SR.GetString(SR.InvalidLowBoundArgumentEx, "ImageIndex", value.ToString(CultureInfo.CurrentCulture), (0).ToString(CultureInfo.CurrentCulture))); } if (ImageIndexer.Index != value) { ImageIndexer.Index = value; if (IsHandleCreated) { RecreateHandle(); } } } } ////// /// The default image index for nodes in the tree view. /// [ SRCategory(SR.CatBehavior), Localizable(true), TypeConverterAttribute(typeof(ImageKeyConverter)), Editor("System.Windows.Forms.Design.ImageIndexEditor, " + AssemblyRef.SystemDesign, typeof(UITypeEditor)), DefaultValue(""), RefreshProperties(RefreshProperties.Repaint), SRDescription(SR.TreeViewImageKeyDescr), RelatedImageList("ImageList") ] public string ImageKey { get { return ImageIndexer.Key; } set { if (ImageIndexer.Key != value) { ImageIndexer.Key = value; if (String.IsNullOrEmpty(value) || value.Equals(SR.GetString(SR.toStringNone))) { ImageIndex = (ImageList != null) ? 0:-1; } if (IsHandleCreated) { RecreateHandle(); } } } } ////// /// Returns the image list control that is bound to the tree view. /// [ SRCategory(SR.CatBehavior), DefaultValue(null), SRDescription(SR.TreeViewImageListDescr), RefreshProperties(RefreshProperties.Repaint) ] public ImageList ImageList { get { return imageList; } set { if (value != imageList) { DetachImageListHandlers(); imageList = value; AttachImageListHandlers(); // Update TreeView's images // if (IsHandleCreated) { SendMessage(NativeMethods.TVM_SETIMAGELIST, 0, value==null? IntPtr.Zero: value.Handle); if (StateImageList != null && StateImageList.Images.Count > 0) { SendMessage(NativeMethods.TVM_SETIMAGELIST, NativeMethods.TVSIL_STATE, internalStateImageList.Handle); } } UpdateCheckedState(root, true); } } } private void AttachImageListHandlers() { if (imageList != null) { //NOTE: any handlers added here should be removed in DetachImageListHandlers imageList.RecreateHandle += new EventHandler(ImageListRecreateHandle); imageList.Disposed += new EventHandler(DetachImageList); imageList.ChangeHandle += new EventHandler(ImageListChangedHandle); } } private void DetachImageListHandlers() { if (imageList != null) { imageList.RecreateHandle -= new EventHandler(ImageListRecreateHandle); imageList.Disposed -= new EventHandler(DetachImageList); imageList.ChangeHandle -= new EventHandler(ImageListChangedHandle); } } private void AttachStateImageListHandlers() { if (stateImageList != null) { //NOTE: any handlers added here should be removed in DetachStateImageListHandlers stateImageList.RecreateHandle += new EventHandler(StateImageListRecreateHandle); stateImageList.Disposed += new EventHandler(DetachStateImageList); stateImageList.ChangeHandle += new EventHandler(StateImageListChangedHandle); } } private void DetachStateImageListHandlers() { if (stateImageList != null) { stateImageList.RecreateHandle -= new EventHandler(StateImageListRecreateHandle); stateImageList.Disposed -= new EventHandler(DetachStateImageList); stateImageList.ChangeHandle -= new EventHandler(StateImageListChangedHandle); } } ////// /// Returns the state image list control that is bound to the tree view. /// [ SRCategory(SR.CatBehavior), DefaultValue(null), SRDescription(SR.TreeViewStateImageListDescr) ] public ImageList StateImageList { get { return stateImageList; } set { if (value != stateImageList) { DetachStateImageListHandlers(); stateImageList = value; if (stateImageList != null && stateImageList.Images.Count > 0) { Image[] images = new Image[stateImageList.Images.Count + 1]; images[0] = stateImageList.Images[0]; for (int i = 1; i <= stateImageList.Images.Count ; i++) { images[i] = stateImageList.Images[i -1]; } internalStateImageList = new ImageList(); internalStateImageList.Images.AddRange(images); } AttachStateImageListHandlers(); // Update TreeView's images // if (IsHandleCreated) { if (stateImageList != null && stateImageList.Images.Count > 0) { Debug.Assert(internalStateImageList != null, "internalStateImageList is null!!"); if (internalStateImageList != null) { SendMessage(NativeMethods.TVM_SETIMAGELIST, NativeMethods.TVSIL_STATE, internalStateImageList.Handle); } } // We need to update the checks // and stateimage value for each node. UpdateCheckedState(root, true); if((value == null || stateImageList.Images.Count == 0) && CheckBoxes) { // Requires Handle Recreate to force on the checkBoxes and states.. RecreateHandle(); } else { // The TreeView shows up the state imageList after sending this message even if the nodes dont have any stateImageIndex set. // In order to avoid that we refresh nodes which would "reset" the images to none. // This causes flicker but gives us the right behavior RefreshNodes(); } } } } } ////// /// The indentation level in pixels. /// [ Localizable(true), SRCategory(SR.CatBehavior), SRDescription(SR.TreeViewIndentDescr) ] public int Indent { get { if (indent != -1) { return indent; } else if (IsHandleCreated) { return unchecked( (int) (long)SendMessage(NativeMethods.TVM_GETINDENT, 0, 0)); } return DefaultTreeViewIndent; } set { if (indent != value) { if (value < 0) { throw new ArgumentOutOfRangeException("Indent", SR.GetString(SR.InvalidLowBoundArgumentEx, "Indent", (value).ToString(CultureInfo.CurrentCulture), (0).ToString(CultureInfo.CurrentCulture))); } if (value > MaxIndent) { throw new ArgumentOutOfRangeException("Indent", SR.GetString(SR.InvalidHighBoundArgumentEx, "Indent", (value).ToString(CultureInfo.CurrentCulture), (MaxIndent).ToString(CultureInfo.CurrentCulture))); } indent = value; if (IsHandleCreated) { SendMessage(NativeMethods.TVM_SETINDENT, value, 0); indent = unchecked( (int) (long)SendMessage(NativeMethods.TVM_GETINDENT, 0, 0)); } } } } ////// /// The height of every item in the tree view, in pixels. /// [ SRCategory(SR.CatAppearance), SRDescription(SR.TreeViewItemHeightDescr) ] public int ItemHeight { get { if (itemHeight != -1) { return itemHeight; } if (IsHandleCreated) { return unchecked( (int) (long)SendMessage(NativeMethods.TVM_GETITEMHEIGHT, 0, 0)); } else { if (CheckBoxes && (DrawMode == TreeViewDrawMode.OwnerDrawAll)) { return Math.Max(16, FontHeight + 3); } return FontHeight + 3; } } set { if (itemHeight != value) { if (value < 1) { throw new ArgumentOutOfRangeException("ItemHeight", SR.GetString(SR.InvalidLowBoundArgumentEx, "ItemHeight", (value).ToString(CultureInfo.CurrentCulture), (1).ToString(CultureInfo.CurrentCulture))); } if (value >= Int16.MaxValue) { throw new ArgumentOutOfRangeException("ItemHeight", SR.GetString(SR.InvalidHighBoundArgument, "ItemHeight", (value).ToString(CultureInfo.CurrentCulture), Int16.MaxValue.ToString(CultureInfo.CurrentCulture))); } itemHeight = value; if (IsHandleCreated) { if (itemHeight % 2 != 0) { setOddHeight = true; try { RecreateHandle(); } finally { setOddHeight = false; } } SendMessage(NativeMethods.TVM_SETITEMHEIGHT, value, 0); itemHeight = unchecked( (int) (long)SendMessage(NativeMethods.TVM_GETITEMHEIGHT, 0, 0)); } } } } ////// /// The LabelEdit property determines if the label text /// of nodes in the tree view is editable. /// [ SRCategory(SR.CatBehavior), DefaultValue(false), SRDescription(SR.TreeViewLabelEditDescr) ] public bool LabelEdit { get { return treeViewState[TREEVIEWSTATE_labelEdit]; } set { if (LabelEdit != value) { treeViewState[TREEVIEWSTATE_labelEdit] = value; if (IsHandleCreated) { UpdateStyles(); } } } } ////// /// This is the color of the lines that connect the nodes of the Treeview. /// [ SRCategory(SR.CatBehavior), SRDescription(SR.TreeViewLineColorDescr), DefaultValue(typeof(Color), "Black") ] public Color LineColor { get { if (IsHandleCreated) { int intColor = unchecked((int)(long)SendMessage(NativeMethods.TVM_GETLINECOLOR, 0, 0)); return ColorTranslator.FromWin32(intColor); } return lineColor; } set { if (lineColor != value) { lineColor = value; if (IsHandleCreated) { SendMessage(NativeMethods.TVM_SETLINECOLOR, 0, ColorTranslator.ToWin32(lineColor)); } } } } ////// /// The collection of nodes associated with this TreeView control /// [ SRCategory(SR.CatBehavior), DesignerSerializationVisibility(DesignerSerializationVisibility.Content), Localizable(true), SRDescription(SR.TreeViewNodesDescr), MergableProperty(false) ] public TreeNodeCollection Nodes { get { if (nodes == null) { nodes = new TreeNodeCollection(root); } return nodes; } } ////// /// [ SRCategory(SR.CatBehavior), DefaultValue(TreeViewDrawMode.Normal), SRDescription(SR.TreeViewDrawModeDescr) ] public TreeViewDrawMode DrawMode { get { return drawMode; } set { //valid values are 0x0 to 0x2 if (!ClientUtils.IsEnumValid(value, (int)value, (int)TreeViewDrawMode.Normal, (int)TreeViewDrawMode.OwnerDrawAll)) { throw new InvalidEnumArgumentException("value", (int)value, typeof(TreeViewDrawMode)); } if (drawMode != value) { drawMode = value; Invalidate(); // We need to invalidate when the Control resizes when the we support custom draw. if (DrawMode == TreeViewDrawMode.OwnerDrawAll) { SetStyle(ControlStyles.ResizeRedraw, true); } } } } ////// Indicates the drawing mode for the tree view. /// ////// /// The delimeter string used by TreeNode.getFullPath(). /// [ SRCategory(SR.CatBehavior), DefaultValue("\\"), SRDescription(SR.TreeViewPathSeparatorDescr) ] public string PathSeparator { get { return pathSeparator; } set { pathSeparator = value; } } ////// /// [ Browsable(false), EditorBrowsable(EditorBrowsableState.Never), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) ] public new Padding Padding { get { return base.Padding; } set { base.Padding = value;} } [ Browsable(false), EditorBrowsable(EditorBrowsableState.Never) ] public new event EventHandler PaddingChanged { add { base.PaddingChanged += value; } remove { base.PaddingChanged -= value; } } ////// ///[To be supplied.] ////// /// This is used for international applications where the language /// is written from RightToLeft. When this property is true, // and the RightToLeft is true, mirroring will be turned on on the form, and /// control placement and text will be from right to left. /// [ SRCategory(SR.CatAppearance), Localizable(true), DefaultValue(false), SRDescription(SR.ControlRightToLeftLayoutDescr) ] public virtual bool RightToLeftLayout { get { return rightToLeftLayout; } set { if (value != rightToLeftLayout) { rightToLeftLayout = value; using(new LayoutTransaction(this, this, PropertyNames.RightToLeftLayout)) { OnRightToLeftLayoutChanged(EventArgs.Empty); } } } } ////// /// [ SRCategory(SR.CatBehavior), DefaultValue(true), SRDescription(SR.TreeViewScrollableDescr) ] public bool Scrollable { get { return treeViewState[TREEVIEWSTATE_scrollable]; } set { if (Scrollable != value) { treeViewState[TREEVIEWSTATE_scrollable] = value; RecreateHandle(); } } } ///[To be supplied.] ////// /// The image index that a node will display when selected. /// The index applies to the ImageList referred to by the imageList property, /// [ DefaultValue(-1), SRCategory(SR.CatBehavior), TypeConverterAttribute(typeof(NoneExcludedImageIndexConverter)), Localizable(true), Editor("System.Windows.Forms.Design.ImageIndexEditor, " + AssemblyRef.SystemDesign, typeof(UITypeEditor)), SRDescription(SR.TreeViewSelectedImageIndexDescr), RelatedImageList("ImageList") ] public int SelectedImageIndex { get { if (imageList == null) { return -1; } if (SelectedImageIndexer.Index >= imageList.Images.Count) { return Math.Max(0, imageList.Images.Count - 1); } return SelectedImageIndexer.Index; } set { // If (none) is selected in the image index editor, we'll just adjust this to // mean image index 0. This is because a treeview must always have an image index - // even if no imagelist exists we want the image index to be 0. // if (value == -1) { value = 0; } if (value < 0) { throw new ArgumentOutOfRangeException("SelectedImageIndex", SR.GetString(SR.InvalidLowBoundArgumentEx, "SelectedImageIndex", value.ToString(CultureInfo.CurrentCulture), (0).ToString(CultureInfo.CurrentCulture))); } if (SelectedImageIndexer.Index != value) { SelectedImageIndexer.Index = value; if (IsHandleCreated) { RecreateHandle(); } } } } ////// /// The default image index for nodes in the tree view. /// [ SRCategory(SR.CatBehavior), Localizable(true), TypeConverterAttribute(typeof(ImageKeyConverter)), Editor("System.Windows.Forms.Design.ImageIndexEditor, " + AssemblyRef.SystemDesign, typeof(UITypeEditor)), DefaultValue(""), RefreshProperties(RefreshProperties.Repaint), SRDescription(SR.TreeViewSelectedImageKeyDescr), RelatedImageList("ImageList") ] public string SelectedImageKey { get { return SelectedImageIndexer.Key; } set { if (SelectedImageIndexer.Key != value) { SelectedImageIndexer.Key = value; if (String.IsNullOrEmpty(value) || value.Equals(SR.GetString(SR.toStringNone))) { SelectedImageIndex = (ImageList != null) ? 0 : -1; } if (IsHandleCreated) { RecreateHandle(); } } } } ////// /// The currently selected tree node, or null if nothing is selected. /// [ SRCategory(SR.CatAppearance), Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), SRDescription(SR.TreeViewSelectedNodeDescr) ] public TreeNode SelectedNode { get { if (IsHandleCreated) { Debug.Assert(selectedNode == null || selectedNode.TreeView != this, "handle is created, but we're still caching selectedNode"); IntPtr hItem = SendMessage(NativeMethods.TVM_GETNEXTITEM, NativeMethods.TVGN_CARET, 0); if (hItem == IntPtr.Zero) return null; return NodeFromHandle(hItem); } else if (selectedNode != null && selectedNode.TreeView == this) { return selectedNode; } else { return null; } } set { if (IsHandleCreated && (value == null || value.TreeView == this)) { // This class invariant is not quite correct -- if the selected node does not belong to this Treeview, // selectedNode != null even though the handle is created. We will call set_SelectedNode // to inform the handle that the selected node has been added to the TreeView. Debug.Assert(selectedNode == null || selectedNode.TreeView != this, "handle is created, but we're still caching selectedNode"); IntPtr hnode = (value == null ? IntPtr.Zero : value.Handle); SendMessage(NativeMethods.TVM_SELECTITEM, NativeMethods.TVGN_CARET, hnode); selectedNode = null; } else { selectedNode = value; } } } ////// /// The ShowLines property determines if lines are drawn between /// nodes in the tree view. /// [ SRCategory(SR.CatBehavior), DefaultValue(true), SRDescription(SR.TreeViewShowLinesDescr) ] public bool ShowLines { get { return treeViewState[TREEVIEWSTATE_showLines]; } set { if (ShowLines != value) { treeViewState[TREEVIEWSTATE_showLines] = value; if (IsHandleCreated) { UpdateStyles(); } } } } ////// /// The ShowLines property determines whether or not the tooltips willbe displayed on the nodes /// [ SRCategory(SR.CatBehavior), DefaultValue(false), SRDescription(SR.TreeViewShowShowNodeToolTipsDescr) ] public bool ShowNodeToolTips { get { return treeViewState[TREEVIEWSTATE_showNodeToolTips]; } set { if (ShowNodeToolTips != value) { treeViewState[TREEVIEWSTATE_showNodeToolTips] = value; if (ShowNodeToolTips) RecreateHandle(); } } } ////// /// The ShowPlusMinus property determines if the "plus/minus" /// expand button is shown next to tree nodes that have children. /// [ SRCategory(SR.CatBehavior), DefaultValue(true), SRDescription(SR.TreeViewShowPlusMinusDescr) ] public bool ShowPlusMinus { get { return treeViewState[TREEVIEWSTATE_showPlusMinus]; } set { if (ShowPlusMinus != value) { treeViewState[TREEVIEWSTATE_showPlusMinus] = value; if (IsHandleCreated) { UpdateStyles(); } } } } ////// /// Determines if lines are draw between nodes at the root of /// the tree view. /// [ SRCategory(SR.CatBehavior), DefaultValue(true), SRDescription(SR.TreeViewShowRootLinesDescr) ] public bool ShowRootLines { get { return treeViewState[TREEVIEWSTATE_showRootLines];} set { if (ShowRootLines != value) { treeViewState[TREEVIEWSTATE_showRootLines] = value; if (IsHandleCreated) { UpdateStyles(); } } } } ////// /// The Sorted property determines if nodes in the tree view are sorted. /// [ SRCategory(SR.CatBehavior), DefaultValue(false), SRDescription(SR.TreeViewSortedDescr), Browsable(false), EditorBrowsable(EditorBrowsableState.Never) ] public bool Sorted { get { return treeViewState[TREEVIEWSTATE_sorted]; } set { if (Sorted != value) { treeViewState[TREEVIEWSTATE_sorted] = value; if (Sorted && TreeViewNodeSorter == null && Nodes.Count >= 1) { RefreshNodes(); } } } } ////// /// The sorting comparer for this TreeView. /// [ SRCategory(SR.CatBehavior), Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), SRDescription(SR.TreeViewNodeSorterDescr) ] public IComparer TreeViewNodeSorter { get { return treeViewNodeSorter; } set { if (treeViewNodeSorter != value) { treeViewNodeSorter = value; if (value != null) { Sort(); } } } } ////// /// [Browsable(false), EditorBrowsable(EditorBrowsableState.Never), Bindable(false)] public override string Text { get { return base.Text; } set { base.Text = value; } } ///[To be supplied.] ////// [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] new public event EventHandler TextChanged { add { base.TextChanged += value; } remove { base.TextChanged -= value; } } /// /// /// The first visible node in the TreeView. Initially /// the first root node is at the top of the TreeView, but if the /// contents have been scrolled another node may be at the top. /// [ SRCategory(SR.CatAppearance), Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), SRDescription(SR.TreeViewTopNodeDescr) ] public TreeNode TopNode { get { if (IsHandleCreated) { IntPtr hitem = SendMessage(NativeMethods.TVM_GETNEXTITEM, NativeMethods.TVGN_FIRSTVISIBLE, 0); return(hitem == IntPtr.Zero ? null : NodeFromHandle(hitem)); } return topNode; } set { if (IsHandleCreated && (value == null || value.TreeView == this)) { // This class invariant is not quite correct -- if the selected node does not belong to this Treeview, // selectedNode != null even though the handle is created. We will call set_SelectedNode // to inform the handle that the selected node has been added to the TreeView. Debug.Assert(topNode == null || topNode.TreeView != this, "handle is created, but we're still caching selectedNode"); IntPtr hnode = (value == null ? IntPtr.Zero : value.Handle); SendMessage(NativeMethods.TVM_SELECTITEM, NativeMethods.TVGN_FIRSTVISIBLE, hnode); topNode = null; } else { topNode = value; } } } ////// /// The count of fully visible nodes in the tree view. This number /// may be greater than the number of nodes in the control. /// The control calculates this value by dividing the height of the /// client window by the height of an item /// [ SRCategory(SR.CatAppearance), Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), SRDescription(SR.TreeViewVisibleCountDescr) ] public int VisibleCount { get { if (IsHandleCreated) return unchecked( (int) (long)SendMessage(NativeMethods.TVM_GETVISIBLECOUNT, 0, 0)); return 0; } } ////// /// [SRCategory(SR.CatBehavior), SRDescription(SR.TreeViewBeforeEditDescr)] public event NodeLabelEditEventHandler BeforeLabelEdit { add { onBeforeLabelEdit += value; } remove { onBeforeLabelEdit -= value; } } ///[To be supplied.] ////// /// [SRCategory(SR.CatBehavior), SRDescription(SR.TreeViewAfterEditDescr)] public event NodeLabelEditEventHandler AfterLabelEdit { add { onAfterLabelEdit += value; } remove { onAfterLabelEdit -= value; } } ///[To be supplied.] ////// /// [SRCategory(SR.CatBehavior), SRDescription(SR.TreeViewBeforeCheckDescr)] public event TreeViewCancelEventHandler BeforeCheck { add { onBeforeCheck += value; } remove { onBeforeCheck -= value; } } ///[To be supplied.] ////// /// [SRCategory(SR.CatBehavior), SRDescription(SR.TreeViewAfterCheckDescr)] public event TreeViewEventHandler AfterCheck { add { onAfterCheck += value; } remove { onAfterCheck -= value; } } ///[To be supplied.] ////// /// [SRCategory(SR.CatBehavior), SRDescription(SR.TreeViewBeforeCollapseDescr)] public event TreeViewCancelEventHandler BeforeCollapse { add { onBeforeCollapse += value; } remove { onBeforeCollapse -= value; } } ///[To be supplied.] ////// /// [SRCategory(SR.CatBehavior), SRDescription(SR.TreeViewAfterCollapseDescr)] public event TreeViewEventHandler AfterCollapse { add { onAfterCollapse += value; } remove { onAfterCollapse -= value; } } ///[To be supplied.] ////// /// [SRCategory(SR.CatBehavior), SRDescription(SR.TreeViewBeforeExpandDescr)] public event TreeViewCancelEventHandler BeforeExpand { add { onBeforeExpand += value; } remove { onBeforeExpand -= value; } } ///[To be supplied.] ////// /// [SRCategory(SR.CatBehavior), SRDescription(SR.TreeViewAfterExpandDescr)] public event TreeViewEventHandler AfterExpand { add { onAfterExpand += value; } remove { onAfterExpand -= value; } } ///[To be supplied.] ////// /// [SRCategory(SR.CatBehavior), SRDescription(SR.TreeViewDrawNodeEventDescr)] public event DrawTreeNodeEventHandler DrawNode { add { onDrawNode += value; } remove { onDrawNode -= value; } } ///Fires when a TreeView node needs to be drawn. ////// /// [SRCategory(SR.CatAction), SRDescription(SR.ListViewItemDragDescr)] public event ItemDragEventHandler ItemDrag { add { onItemDrag += value; } remove { onItemDrag -= value; } } ///[To be supplied.] ////// /// [SRCategory(SR.CatAction), SRDescription(SR.TreeViewNodeMouseHoverDescr)] public event TreeNodeMouseHoverEventHandler NodeMouseHover { add { onNodeMouseHover += value; } remove { onNodeMouseHover -= value; } } ///[To be supplied.] ////// /// [SRCategory(SR.CatBehavior), SRDescription(SR.TreeViewBeforeSelectDescr)] public event TreeViewCancelEventHandler BeforeSelect { add { onBeforeSelect += value; } remove { onBeforeSelect -= value; } } ///[To be supplied.] ////// /// [SRCategory(SR.CatBehavior), SRDescription(SR.TreeViewAfterSelectDescr)] public event TreeViewEventHandler AfterSelect { add { onAfterSelect += value; } remove { onAfterSelect -= value; } } ///[To be supplied.] ////// /// TreeView Onpaint. /// ///[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] public new event PaintEventHandler Paint { add { base.Paint += value; } remove { base.Paint -= value; } } /// /// /// [SRCategory(SR.CatBehavior), SRDescription(SR.TreeViewNodeMouseClickDescr)] public event TreeNodeMouseClickEventHandler NodeMouseClick { add { onNodeMouseClick += value; } remove { onNodeMouseClick -= value; } } ///[To be supplied.] ////// /// [SRCategory(SR.CatBehavior), SRDescription(SR.TreeViewNodeMouseDoubleClickDescr)] public event TreeNodeMouseClickEventHandler NodeMouseDoubleClick { add { onNodeMouseDoubleClick += value; } remove { onNodeMouseDoubleClick -= value; } } ///[To be supplied.] ////// /// [SRCategory(SR.CatPropertyChanged), SRDescription(SR.ControlOnRightToLeftLayoutChangedDescr)] public event EventHandler RightToLeftLayoutChanged { add { onRightToLeftLayoutChanged += value; } remove { onRightToLeftLayoutChanged -= value; } } ///[To be supplied.] ////// /// Disables redrawing of the tree view. A call to beginUpdate() must be /// balanced by a following call to endUpdate(). Following a call to /// beginUpdate(), any redrawing caused by operations performed on the /// tree view is deferred until the call to endUpdate(). /// public void BeginUpdate() { BeginUpdateInternal(); } ////// /// Collapses all nodes at the root level. /// public void CollapseAll() { root.Collapse(); } ////// /// ///protected override void CreateHandle() { if (!RecreatingHandle) { IntPtr userCookie = UnsafeNativeMethods.ThemingScope.Activate(); try { NativeMethods.INITCOMMONCONTROLSEX icc = new NativeMethods.INITCOMMONCONTROLSEX(); icc.dwICC = NativeMethods.ICC_TREEVIEW_CLASSES; SafeNativeMethods.InitCommonControlsEx(icc); } finally { UnsafeNativeMethods.ThemingScope.Deactivate(userCookie); } } base.CreateHandle(); } /// /// /// Resets the imageList to null. We wire this method up to the imageList's /// Dispose event, so that we don't hang onto an imageList that's gone away. /// ///private void DetachImageList(object sender, EventArgs e) { ImageList = null; } /// /// /// Resets the stateimageList to null. We wire this method up to the stateimageList's /// Dispose event, so that we don't hang onto an stateimageList that's gone away. /// ///private void DetachStateImageList(object sender, EventArgs e) { internalStateImageList = null; StateImageList = null; } /// /// /// ///protected override void Dispose(bool disposing) { if (disposing) { foreach (TreeNode node in Nodes) { node.ContextMenu = null; } // lock(this) { DetachImageListHandlers(); imageList = null; DetachStateImageListHandlers(); stateImageList = null; } } base.Dispose(disposing); } /// /// /// Reenables redrawing of the tree view. A call to beginUpdate() must be /// balanced by a following call to endUpdate(). Following a call to /// beginUpdate(), any redrawing caused by operations performed on the /// combo box is deferred until the call to endUpdate(). /// public void EndUpdate() { EndUpdateInternal(); } ////// /// Expands all nodes at the root level. /// public void ExpandAll() { root.ExpandAll(); } ////// /// Forces the TreeView to recalculate all its nodes widths so that it updates the /// scrollbars as appropriate. /// ///internal void ForceScrollbarUpdate(bool delayed) { // ForceScrollbarUpdate call WM_SETREDRAW( FALSE ) followed by WM_SETREDRAW( TRUE ) // So if TreeView.BeginUpdate is called // ForceScrollbarUpdate effectively causes tree view to ignore BeginUpdate and cause control to update on every change. // So gaurd against this scenario by using the new internal method on Control. if (!IsUpdating()) { if (IsHandleCreated) { SendMessage(NativeMethods.WM_SETREDRAW, 0, 0); if (delayed) UnsafeNativeMethods.PostMessage(new HandleRef(this, Handle), NativeMethods.WM_SETREDRAW, (IntPtr)1, IntPtr.Zero); else SendMessage(NativeMethods.WM_SETREDRAW, 1, 0); } } } /// /// /// Called by ToolTip to poke in that Tooltip into this ComCtl so that the Native ChildToolTip is not exposed. /// ///internal void SetToolTip(ToolTip toolTip, string toolTipText) { if (toolTip != null) { UnsafeNativeMethods.SendMessage(new HandleRef(toolTip, toolTip.Handle), NativeMethods.TTM_SETMAXTIPWIDTH, 0, SystemInformation.MaxWindowTrackSize.Width); UnsafeNativeMethods.SendMessage(new HandleRef(this, this.Handle), NativeMethods.TVM_SETTOOLTIPS, new HandleRef(toolTip, toolTip.Handle), 0); controlToolTipText = toolTipText; } } /// /// /// Gives the information about which part of the treeNode is at the given point. /// public TreeViewHitTestInfo HitTest(Point pt) { return HitTest(pt.X, pt.Y); } ////// /// Gives the information about which part of the treeNode is at the given x, y. /// public TreeViewHitTestInfo HitTest(int x, int y) { NativeMethods.TV_HITTESTINFO tvhi = new NativeMethods.TV_HITTESTINFO(); tvhi.pt_x = x; tvhi.pt_y = y; IntPtr hnode = UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TVM_HITTEST, 0, tvhi); TreeNode node = (hnode == IntPtr.Zero ? null : NodeFromHandle(hnode)); TreeViewHitTestLocations loc = (TreeViewHitTestLocations)tvhi.flags; return (new TreeViewHitTestInfo(node, loc)); } ////// /// Defined so that a tree node can use it /// /// ///internal bool TreeViewBeforeCheck(TreeNode node, TreeViewAction actionTaken) { TreeViewCancelEventArgs tvce = new TreeViewCancelEventArgs(node, false, actionTaken); OnBeforeCheck(tvce); return (tvce.Cancel); } internal void TreeViewAfterCheck(TreeNode node, TreeViewAction actionTaken) { OnAfterCheck(new TreeViewEventArgs(node, actionTaken)); } /// /// /// Returns count of nodes at root, optionally including all subtrees. /// public int GetNodeCount(bool includeSubTrees) { return root.GetNodeCount(includeSubTrees); } ////// /// Returns the TreeNode at the given location in tree view coordinates. /// public TreeNode GetNodeAt(Point pt) { return GetNodeAt(pt.X, pt.Y); } ////// /// Returns the TreeNode at the given location in tree view coordinates. /// public TreeNode GetNodeAt(int x, int y) { NativeMethods.TV_HITTESTINFO tvhi = new NativeMethods.TV_HITTESTINFO(); tvhi.pt_x = x; tvhi.pt_y = y; IntPtr hnode = UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TVM_HITTEST, 0, tvhi); return(hnode == IntPtr.Zero ? null : NodeFromHandle(hnode)); } private void ImageListRecreateHandle(object sender, EventArgs e) { if (IsHandleCreated) { IntPtr handle = (ImageList == null) ? IntPtr.Zero : ImageList.Handle; SendMessage(NativeMethods.TVM_SETIMAGELIST, 0, handle); } } private void UpdateImagesRecursive ( TreeNode node ) { node.UpdateImage(); // VSWhidbey 208200: Iterate only through the Nodes collection rather than the // array since an item might have been removed from the collection, and // correspondingly "removed" from the array, but still exist in the array // since the array isn't actually re-dimensioned down to a smaller size. foreach (TreeNode child in node.Nodes) { UpdateImagesRecursive(child); } } private void ImageListChangedHandle(object sender, EventArgs e) { if ((null != sender) && (sender == imageList) && IsHandleCreated) { BeginUpdate(); foreach (TreeNode node in Nodes) { UpdateImagesRecursive(node); } EndUpdate(); } } private void StateImageListRecreateHandle(object sender, EventArgs e) { if (IsHandleCreated) { IntPtr handle = IntPtr.Zero; if (internalStateImageList != null) { handle = internalStateImageList.Handle; } SendMessage(NativeMethods.TVM_SETIMAGELIST, NativeMethods.TVSIL_STATE, handle); } } private void StateImageListChangedHandle(object sender, EventArgs e) { if ((null != sender) && (sender == stateImageList) && IsHandleCreated) { // Since the native treeview requires the state imagelist to be 1-indexed we need to // re add the images if the original collection had changed. if (stateImageList != null && stateImageList.Images.Count > 0) { Image[] images = new Image[stateImageList.Images.Count + 1]; images[0] = stateImageList.Images[0]; for (int i = 1; i <= stateImageList.Images.Count ; i++) { images[i] = stateImageList.Images[i -1]; } if (internalStateImageList != null) { internalStateImageList.Images.Clear(); internalStateImageList.Images.AddRange(images); } else { internalStateImageList = new ImageList(); internalStateImageList.Images.AddRange(images); } Debug.Assert(internalStateImageList != null, "Why are changing images when the Imagelist is null?"); if (internalStateImageList != null) { SendMessage(NativeMethods.TVM_SETIMAGELIST, NativeMethods.TVSIL_STATE, internalStateImageList.Handle); } } else //stateImageList == null || stateImageList.Images.Count = 0; { UpdateCheckedState(root, true); } } } ////// /// /// protected override bool IsInputKey(Keys keyData) { // If in edit mode, treat Return as an input key, so the form doesn't grab it // and treat it as clicking the Form.AcceptButton. Similarly for Escape // and Form.CancelButton. if (editNode != null && (keyData & Keys.Alt) == 0) { switch (keyData & Keys.KeyCode) { case Keys.Return: case Keys.Escape: case Keys.PageUp: case Keys.PageDown: case Keys.Home: case Keys.End: return true; } } return base.IsInputKey(keyData); } ////// Overridden to handle RETURN key. /// ////// /// Note this can be null - particularly if any windows messages get generated during /// the insertion of a tree node (TVM_INSERTITEM) /// ///internal TreeNode NodeFromHandle(IntPtr handle) { TreeNode node = (TreeNode)nodeTable[handle]; return node; } /// /// /// Fires the DrawNode event. /// protected virtual void OnDrawNode(DrawTreeNodeEventArgs e) { if (onDrawNode != null) onDrawNode(this, e); } ////// /// ///protected override void OnHandleCreated(EventArgs e) { TreeNode savedSelectedNode = this.selectedNode; this.selectedNode = null; base.OnHandleCreated(e); //ComCtl 5 has some bug fixes that, to enable, require us to send the control //a CCM_SETVERSION with 5 as the version. The one we want in particular is //the fix for the node clipping issue when a font is set by means of CDRF_NEWFONT. //The fix is not perfect, but the behavior is better than before. int version = unchecked((int)(long)SendMessage(NativeMethods.CCM_GETVERSION, 0, 0)); if (version < 5) { SendMessage(NativeMethods.CCM_SETVERSION, 5, 0); } // Workaround for problem in TreeView where it doesn't recognize the TVS_CHECKBOXES // style if it is set before the window is created. To get around the problem, // we set it here after the window is created, and we make sure we don't set it // in getCreateParams so that this will actually change the value of the bit. // This seems to make the Treeview happy. if (CheckBoxes) { int style = unchecked((int)(UnsafeNativeMethods.GetWindowLong(new HandleRef(this, Handle), NativeMethods.GWL_STYLE))); style |= NativeMethods.TVS_CHECKBOXES; UnsafeNativeMethods.SetWindowLong(new HandleRef(this, Handle), NativeMethods.GWL_STYLE, new HandleRef(null, (IntPtr)style)); } if (ShowNodeToolTips && !DesignMode) { int style = unchecked((int)(UnsafeNativeMethods.GetWindowLong(new HandleRef(this, Handle), NativeMethods.GWL_STYLE))); style |= NativeMethods.TVS_INFOTIP; UnsafeNativeMethods.SetWindowLong(new HandleRef(this, Handle), NativeMethods.GWL_STYLE, new HandleRef(null, (IntPtr)style)); } Color c; c = BackColor; if (c != SystemColors.Window) SendMessage(NativeMethods.TVM_SETBKCOLOR, 0, ColorTranslator.ToWin32(c)); c = ForeColor; if (c != SystemColors.WindowText) SendMessage(NativeMethods.TVM_SETTEXTCOLOR, 0, ColorTranslator.ToWin32(c)); /// put the linecolor into the native control only if Set ... if (lineColor != Color.Empty) { SendMessage(NativeMethods.TVM_SETLINECOLOR, 0, ColorTranslator.ToWin32(lineColor)); } if (imageList != null) SendMessage(NativeMethods.TVM_SETIMAGELIST, 0, imageList.Handle); if (stateImageList != null && stateImageList.Images.Count > 0) { Image[] images = new Image[stateImageList.Images.Count + 1]; images[0] = stateImageList.Images[0]; for (int i = 1; i <= stateImageList.Images.Count ; i++) { images[i] = stateImageList.Images[i -1]; } internalStateImageList = new ImageList(); internalStateImageList.Images.AddRange(images); SendMessage(NativeMethods.TVM_SETIMAGELIST, NativeMethods.TVSIL_STATE, internalStateImageList.Handle); } if (indent != -1) { SendMessage(NativeMethods.TVM_SETINDENT, indent, 0); } if (itemHeight != -1) { SendMessage(NativeMethods.TVM_SETITEMHEIGHT, ItemHeight, 0); } // Fix for bug 331158. Essentially we are setting the width to be infinite so that the // TreeView never thinks it needs a scrollbar when the first node is created // during the first handle creation. // // This is set back to the oldSize after the Realize method. int oldSize = 0; try { treeViewState[TREEVIEWSTATE_stopResizeWindowMsgs] = true; oldSize = this.Width; int flags = NativeMethods.SWP_NOZORDER | NativeMethods.SWP_NOACTIVATE | NativeMethods.SWP_NOMOVE; SafeNativeMethods.SetWindowPos(new HandleRef(this, this.Handle), NativeMethods.NullHandleRef, this.Left, this.Top, Int32.MaxValue, this.Height, flags); root.Realize(false); if (oldSize != 0) { SafeNativeMethods.SetWindowPos(new HandleRef(this, this.Handle), NativeMethods.NullHandleRef, this.Left, this.Top, oldSize, this.Height, flags); } } finally { treeViewState[TREEVIEWSTATE_stopResizeWindowMsgs] = false; } SelectedNode = savedSelectedNode; } /// /// /// ///protected override void OnHandleDestroyed(EventArgs e) { selectedNode = SelectedNode; base.OnHandleDestroyed(e); } /// /// /// We keep track of if we've hovered already so we don't fire multiple hover events /// ///protected override void OnMouseLeave(EventArgs e) { hoveredAlready = false; base.OnMouseLeave(e); } /// /// /// In order for the MouseHover event to fire for each item in a TreeView, /// the node the mouse is hovering over is found. Each time a new node is hovered /// over a new event is raised. /// protected override void OnMouseHover(EventArgs e) { /// Hover events need to be caught for each node /// within the TreeView so the appropriate /// NodeHovered event can be raised. NativeMethods.TV_HITTESTINFO tvhip = new NativeMethods.TV_HITTESTINFO(); Point pos = Cursor.Position; pos = PointToClientInternal(pos); tvhip.pt_x = pos.X; tvhip.pt_y = pos.Y; IntPtr hnode = UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TVM_HITTEST, 0, tvhip); if (hnode != IntPtr.Zero && ((tvhip.flags & NativeMethods.TVHT_ONITEM) != 0)) { TreeNode tn = NodeFromHandle(hnode); if (tn != prevHoveredNode && tn != null) { OnNodeMouseHover( new TreeNodeMouseHoverEventArgs(tn)); prevHoveredNode = tn; } } if (!hoveredAlready) { base.OnMouseHover(e); hoveredAlready = true; } ResetMouseEventArgs(); } ////// /// Fires the beforeLabelEdit event. /// protected virtual void OnBeforeLabelEdit(NodeLabelEditEventArgs e) { if (onBeforeLabelEdit != null) onBeforeLabelEdit(this, e); } ////// /// Fires the afterLabelEdit event. /// protected virtual void OnAfterLabelEdit(NodeLabelEditEventArgs e) { if (onAfterLabelEdit != null) onAfterLabelEdit(this, e); } ////// /// Fires the beforeCheck event. /// protected virtual void OnBeforeCheck(TreeViewCancelEventArgs e) { if (onBeforeCheck != null) onBeforeCheck(this, e); } ////// /// Fires the afterCheck event. /// protected virtual void OnAfterCheck(TreeViewEventArgs e) { if (onAfterCheck != null) onAfterCheck(this, e); } ////// /// Fires the beforeCollapse event. /// protected internal virtual void OnBeforeCollapse(TreeViewCancelEventArgs e) { if (onBeforeCollapse != null) onBeforeCollapse(this, e); } ////// /// Fires the afterCollapse event. /// protected internal virtual void OnAfterCollapse(TreeViewEventArgs e) { if (onAfterCollapse != null) onAfterCollapse(this, e); } ////// /// Fires the beforeExpand event. /// protected virtual void OnBeforeExpand(TreeViewCancelEventArgs e) { if (onBeforeExpand != null) onBeforeExpand(this, e); } ////// /// Fires the afterExpand event. /// protected virtual void OnAfterExpand(TreeViewEventArgs e) { if (onAfterExpand != null) onAfterExpand(this, e); } ////// /// Fires the ItemDrag event. /// protected virtual void OnItemDrag(ItemDragEventArgs e) { if (onItemDrag != null) onItemDrag(this, e); } ////// /// Fires the NodeMouseHover event. /// protected virtual void OnNodeMouseHover(TreeNodeMouseHoverEventArgs e) { if (onNodeMouseHover != null) onNodeMouseHover(this, e); } ////// /// Fires the beforeSelect event. /// protected virtual void OnBeforeSelect(TreeViewCancelEventArgs e) { if (onBeforeSelect != null) onBeforeSelect(this, e); } ////// /// Fires the afterSelect event. /// protected virtual void OnAfterSelect(TreeViewEventArgs e) { if (onAfterSelect != null) onAfterSelect(this, e); } ////// /// Fires the onNodeMouseClick event. /// protected virtual void OnNodeMouseClick(TreeNodeMouseClickEventArgs e) { if (onNodeMouseClick != null) onNodeMouseClick(this, e); } ////// /// Fires the onNodeMouseDoubleClick event. /// protected virtual void OnNodeMouseDoubleClick(TreeNodeMouseClickEventArgs e) { if (onNodeMouseDoubleClick != null) onNodeMouseDoubleClick(this, e); } ////// /// Handles the OnBeforeCheck / OnAfterCheck for keyboard clicks /// ///protected override void OnKeyDown(KeyEventArgs e) { base.OnKeyDown(e); if (e.Handled) return; // if it's a space, send the check notifications and toggle the checkbox if we're not // cancelled. if (CheckBoxes && (e.KeyData & Keys.KeyCode) == Keys.Space) { TreeNode node = this.SelectedNode; if (node != null) { bool eventReturn = TreeViewBeforeCheck(node, TreeViewAction.ByKeyboard); if (!eventReturn) { node.CheckedInternal = !node.CheckedInternal; TreeViewAfterCheck(node, TreeViewAction.ByKeyboard); } e.Handled = true; return; } } } /// /// /// Handles the OnBeforeCheck / OnAfterCheck for keyboard clicks /// ///protected override void OnKeyUp(KeyEventArgs e) { base.OnKeyUp(e); if (e.Handled) return; // eat the space key if ((e.KeyData & Keys.KeyCode) == Keys.Space) { e.Handled = true; return; } } /// /// /// Handles the OnBeforeCheck / OnAfterCheck for keyboard clicks /// ///protected override void OnKeyPress(KeyPressEventArgs e) { base.OnKeyPress(e); if (e.Handled) return; // eat the space key if (e.KeyChar == ' ') e.Handled = true; } /// /// /// [EditorBrowsable(EditorBrowsableState.Advanced)] protected virtual void OnRightToLeftLayoutChanged(EventArgs e) { if (GetAnyDisposingInHierarchy()) { return; } if (RightToLeft == RightToLeft.Yes) { RecreateHandle(); } if (onRightToLeftLayoutChanged != null) { onRightToLeftLayoutChanged(this, e); } } // Refresh the nodes by clearing the tree and adding the nodes back again // private void RefreshNodes() { TreeNode[] nodes = new TreeNode[Nodes.Count]; Nodes.CopyTo(nodes, 0); Nodes.Clear(); Nodes.AddRange(nodes); } ///[To be supplied.] ////// /// This resets the indentation to the system default. /// private void ResetIndent() { indent = -1; // is this overkill? RecreateHandle(); } ////// /// This resets the item height to the system default. /// private void ResetItemHeight() { itemHeight = -1; RecreateHandle(); } ////// /// Retrieves true if the indent should be persisted in code gen. /// private bool ShouldSerializeIndent() { return(indent != -1); } ////// /// Retrieves true if the itemHeight should be persisted in code gen. /// private bool ShouldSerializeItemHeight() { return(itemHeight != -1); } private bool ShouldSerializeSelectedImageIndex() { if (imageList != null) { return (SelectedImageIndex != 0); } return (SelectedImageIndex != -1); } private bool ShouldSerializeImageIndex() { if (imageList != null) { return (ImageIndex != 0); } return (ImageIndex != -1); } ////// /// Updated the sorted order /// public void Sort() { Sorted = true; RefreshNodes(); } ////// /// Returns a string representation for this control. /// ///public override string ToString() { string s = base.ToString(); if (Nodes != null) { s += ", Nodes.Count: " + Nodes.Count.ToString(CultureInfo.CurrentCulture); if (Nodes.Count > 0) s += ", Nodes[0]: " + Nodes[0].ToString(); } return s; } private unsafe void TvnBeginDrag(MouseButtons buttons, NativeMethods.NMTREEVIEW* nmtv) { NativeMethods.TV_ITEM item = nmtv->itemNew; // Check for invalid node handle if (item.hItem == IntPtr.Zero) { return; } TreeNode node = NodeFromHandle(item.hItem); OnItemDrag(new ItemDragEventArgs(buttons, node)); } private unsafe IntPtr TvnExpanding(NativeMethods.NMTREEVIEW* nmtv) { NativeMethods.TV_ITEM item = nmtv->itemNew; // Check for invalid node handle if (item.hItem == IntPtr.Zero) { return IntPtr.Zero; } TreeViewCancelEventArgs e = null; if ((item.state & NativeMethods.TVIS_EXPANDED) == 0) { e = new TreeViewCancelEventArgs(NodeFromHandle(item.hItem), false, TreeViewAction.Expand); OnBeforeExpand(e); } else { e = new TreeViewCancelEventArgs(NodeFromHandle(item.hItem), false, TreeViewAction.Collapse); OnBeforeCollapse(e); } return (IntPtr)(e.Cancel? 1: 0); } private unsafe void TvnExpanded(NativeMethods.NMTREEVIEW* nmtv) { NativeMethods.TV_ITEM item = nmtv->itemNew; // Check for invalid node handle if (item.hItem == IntPtr.Zero) { return; } TreeViewEventArgs e; TreeNode node = NodeFromHandle(item.hItem); // Note that IsExpanded is invalid for the moment, so we use item item.state to branch. if ((item.state & NativeMethods.TVIS_EXPANDED) == 0) { e = new TreeViewEventArgs(node, TreeViewAction.Collapse); OnAfterCollapse(e); } else { e = new TreeViewEventArgs(node, TreeViewAction.Expand); OnAfterExpand(e); } } private unsafe IntPtr TvnSelecting(NativeMethods.NMTREEVIEW* nmtv) { if (treeViewState[ TREEVIEWSTATE_ignoreSelects]) { return (IntPtr)1; } // Check for invalid node handle if (nmtv->itemNew.hItem == IntPtr.Zero) { return IntPtr.Zero; } TreeNode node = NodeFromHandle(nmtv->itemNew.hItem); TreeViewAction action = TreeViewAction.Unknown; switch(nmtv->action) { case NativeMethods.TVC_BYKEYBOARD: action = TreeViewAction.ByKeyboard; break; case NativeMethods.TVC_BYMOUSE: action = TreeViewAction.ByMouse; break; } TreeViewCancelEventArgs e = new TreeViewCancelEventArgs(node, false, action); OnBeforeSelect(e); return (IntPtr)(e.Cancel? 1: 0); } private unsafe void TvnSelected(NativeMethods.NMTREEVIEW* nmtv) { if (nodesCollectionClear) //if called thru the Clear( ) of treeNodeCollection then just return... { return; } if (nmtv->itemNew.hItem != IntPtr.Zero) { TreeViewAction action = TreeViewAction.Unknown; switch(nmtv->action) { case NativeMethods.TVC_BYKEYBOARD: action = TreeViewAction.ByKeyboard; break; case NativeMethods.TVC_BYMOUSE: action = TreeViewAction.ByMouse; break; } OnAfterSelect(new TreeViewEventArgs(NodeFromHandle(nmtv->itemNew.hItem), action)); } // TreeView doesn't properly revert back to the unselected image // if the unselected image is blank. // NativeMethods.RECT rc = new NativeMethods.RECT(); *((IntPtr *) &rc.left) = nmtv->itemOld.hItem; if (nmtv->itemOld.hItem != IntPtr.Zero) { if (unchecked( (int) (long)UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TVM_GETITEMRECT, 1, ref rc)) != 0) SafeNativeMethods.InvalidateRect(new HandleRef(this, Handle), ref rc, true); } } private IntPtr TvnBeginLabelEdit(NativeMethods.NMTVDISPINFO nmtvdi) { // Check for invalid node handle if (nmtvdi.item.hItem == IntPtr.Zero) { return IntPtr.Zero; } TreeNode editingNode = NodeFromHandle(nmtvdi.item.hItem); NodeLabelEditEventArgs e = new NodeLabelEditEventArgs(editingNode); OnBeforeLabelEdit(e); if (!e.CancelEdit) editNode = editingNode; return (IntPtr)(e.CancelEdit ? 1 : 0); } private IntPtr TvnEndLabelEdit(NativeMethods.NMTVDISPINFO nmtvdi) { editNode = null; // Check for invalid node handle if (nmtvdi.item.hItem == IntPtr.Zero) { return (IntPtr)1; } TreeNode node = NodeFromHandle(nmtvdi.item.hItem); string newText = (nmtvdi.item.pszText == IntPtr.Zero ? null : Marshal.PtrToStringAuto(nmtvdi.item.pszText)); NodeLabelEditEventArgs e = new NodeLabelEditEventArgs(node, newText); OnAfterLabelEdit(e); if (newText != null && !e.CancelEdit && node != null) { node.text = newText; if (Scrollable) ForceScrollbarUpdate(true); } return (IntPtr)(e.CancelEdit ? 0 : 1); } internal override void UpdateStylesCore() { base.UpdateStylesCore(); if (IsHandleCreated && CheckBoxes) { if (StateImageList != null) { // Setting the TVS_CHECKBOXES window style also causes the TreeView to display the default checkbox // images rather than the user specified StateImageList. We send a TVM_SETIMAGELIST to restore the // user's images. if (internalStateImageList != null) { SendMessage(NativeMethods.TVM_SETIMAGELIST, NativeMethods.TVSIL_STATE, internalStateImageList.Handle); } } } } // Setting the NativeMethods.TVS_CHECKBOXES style clears the checked state private void UpdateCheckedState(TreeNode node, bool update) { // This looks funny, but CheckedInternal returns the cached isChecked value and the internal // setter will blindly issue TVM_SETITEM so this gets us back in [....]. if (update) { node.CheckedInternal = node.CheckedInternal; for (int i = node.Nodes.Count - 1; i >= 0; i--) { UpdateCheckedState(node.Nodes[i], update); } } else { node.CheckedInternal = false; for (int i = node.Nodes.Count - 1; i >= 0; i--) { UpdateCheckedState(node.Nodes[i], update); } } } private void WmMouseDown(ref Message m, MouseButtons button, int clicks) { // Required to put the TreeView in sane-state for painting proper highlighting of selectedNodes. // If the user shows the ContextMenu bu overiding the WndProc( ), then the treeview // goes into the weird state where the high-light gets locked to the node on which the ContextMenu was shown. // So we need to get the native TREEIVEW out of this weird state. // Refer to VSWhidbey : 249191. SendMessage(NativeMethods.TVM_SELECTITEM, NativeMethods.TVGN_DROPHILITE, null); // Windows TreeView pushes its own message loop in WM_xBUTTONDOWN, so fire the // event before calling defWndProc or else it won't get fired until the button // comes back up. OnMouseDown(new MouseEventArgs(button, clicks, NativeMethods.Util.SignedLOWORD(m.LParam), NativeMethods.Util.SignedHIWORD(m.LParam), 0)); //If Validation is cancelled dont fire any events through the Windows TreeView's message loop... if (!ValidationCancelled) { DefWndProc(ref m); } } /// /// /// Performs custom draw handling /// ///private void CustomDraw(ref Message m) { NativeMethods.NMTVCUSTOMDRAW nmcd = (NativeMethods.NMTVCUSTOMDRAW)m.GetLParam(typeof(NativeMethods.NMTVCUSTOMDRAW)); // Find out which stage we're drawing switch (nmcd.nmcd.dwDrawStage) { // Do we want OwnerDraw for this paint cycle? case NativeMethods.CDDS_PREPAINT: m.Result = (IntPtr)NativeMethods.CDRF_NOTIFYITEMDRAW; // yes, we do... return; // We've got opt-in on owner draw for items - so handle each one. case NativeMethods.CDDS_ITEMPREPAINT: // get the node Debug.Assert(nmcd.nmcd.dwItemSpec != IntPtr.Zero, "Invalid node handle in ITEMPREPAINT"); TreeNode node = NodeFromHandle((IntPtr)nmcd.nmcd.dwItemSpec); if (node == null) { // this can happen if we are presently inserting the node - it hasn't yet // been added to the handle table m.Result = (IntPtr)(NativeMethods.CDRF_SKIPDEFAULT); return; } int state = nmcd.nmcd.uItemState; // The commctrl TreeView allows you to draw the whole row of a node // or nothing at all. The way we provide OwnerDrawText is by asking it // to draw everything but the text - to do this, we set text color same // as background color. if (drawMode == TreeViewDrawMode.OwnerDrawText) { nmcd.clrText = nmcd.clrTextBk; Marshal.StructureToPtr(nmcd, m.LParam, false); m.Result = (IntPtr) (NativeMethods.CDRF_NEWFONT | NativeMethods.CDRF_NOTIFYPOSTPAINT); return; } else if (drawMode == TreeViewDrawMode.OwnerDrawAll) { Graphics g = Graphics.FromHdcInternal(nmcd.nmcd.hdc); DrawTreeNodeEventArgs e; try { Rectangle bounds = node.RowBounds; NativeMethods.SCROLLINFO si = new NativeMethods.SCROLLINFO(); si.cbSize = Marshal.SizeOf(typeof(NativeMethods.SCROLLINFO)); si.fMask = NativeMethods.SIF_POS; if (UnsafeNativeMethods.GetScrollInfo(new HandleRef(this, Handle), NativeMethods.SB_HORZ,si) != false) { // VsW : 432718 // need to get the correct bounds if horizontal scroll bar is shown. // In this case the bounds.X needs to be negative and width needs to be updated to the increased width (scrolled region). int value = si.nPos; if (value > 0) { bounds.X -= value; bounds.Width += value; } } e = new DrawTreeNodeEventArgs(g, node, bounds, (TreeNodeStates) (state)); OnDrawNode(e); } finally { g.Dispose(); } if (!e.DrawDefault) { m.Result = (IntPtr)(NativeMethods.CDRF_SKIPDEFAULT); return; } } //TreeViewDrawMode.Normal case #if DEBUGGING // Diagnostic output Debug.WriteLine("Itemstate: "+state); Debug.WriteLine("Itemstate: "+ "\nDISABLED" + (((state & NativeMethods.CDIS_DISABLED) != 0) ? "TRUE" : "FALSE") + "\nHOT" + (((state & NativeMethods.CDIS_HOT) != 0) ? "TRUE" : "FALSE") + "\nGRAYED" + (((state & NativeMethods.CDIS_GRAYED) != 0) ? "TRUE" : "FALSE") + "\nSELECTED" + (((state & NativeMethods.CDIS_SELECTED) != 0) ? "TRUE" : "FALSE") + "\nFOCUS" + (((state & NativeMethods.CDIS_FOCUS) != 0) ? "TRUE" : "FALSE") + "\nDEFAULT" + (((state & NativeMethods.CDIS_DEFAULT) != 0) ? "TRUE" : "FALSE") + "\nMARKED" + (((state & NativeMethods.CDIS_MARKED) != 0) ? "TRUE" : "FALSE") + "\nINDETERMINATE" + (((state & NativeMethods.CDIS_INDETERMINATE) != 0) ? "TRUE" : "FALSE")); #endif OwnerDrawPropertyBag renderinfo = GetItemRenderStyles(node,state); // TreeView has problems with drawing items at times; it gets confused // as to which colors apply to which items (see focus rectangle shifting; // when one item is selected, click and hold on another). This needs to be fixed. bool colordelta = false; Color riFore = renderinfo.ForeColor; Color riBack = renderinfo.BackColor; if (renderinfo != null && !riFore.IsEmpty) { nmcd.clrText = ColorTranslator.ToWin32(riFore); colordelta = true; } if (renderinfo != null && !riBack.IsEmpty) { nmcd.clrTextBk = ColorTranslator.ToWin32(riBack); colordelta = true; } if (colordelta) { Marshal.StructureToPtr(nmcd, m.LParam, false); } if (renderinfo != null && renderinfo.Font != null) { // Mess with the DC directly... SafeNativeMethods.SelectObject(new HandleRef(nmcd.nmcd, nmcd.nmcd.hdc), new HandleRef(renderinfo, renderinfo.FontHandle)); // There is a problem in winctl that clips node fonts if the fontsize // is larger than the treeview font size. The behavior is much better in comctl 5 and above. m.Result = (IntPtr)NativeMethods.CDRF_NEWFONT; return; } // fall through and do the default drawing work goto default; case (NativeMethods.CDDS_ITEMPOSTPAINT): //User draws only the text in OwnerDrawText mode, as explained in comments above if (drawMode == TreeViewDrawMode.OwnerDrawText) { Debug.Assert(nmcd.nmcd.dwItemSpec != IntPtr.Zero, "Invalid node handle in ITEMPOSTPAINT"); // Get the node node = NodeFromHandle((IntPtr)nmcd.nmcd.dwItemSpec); if (node == null) { // this can happen if we are presently inserting the node - it hasn't yet // been added to the handle table return; } Graphics g = Graphics.FromHdcInternal(nmcd.nmcd.hdc); DrawTreeNodeEventArgs e; try { Rectangle bounds = node.Bounds; Size textSize = TextRenderer.MeasureText(node.Text, node.TreeView.Font); Point textLoc = new Point(bounds.X -1, bounds.Y); // required to center the text bounds = new Rectangle(textLoc, new Size(textSize.Width, bounds.Height)); e = new DrawTreeNodeEventArgs(g, node, bounds, (TreeNodeStates) (nmcd.nmcd.uItemState)); OnDrawNode(e); if (e.DrawDefault) { //Simulate default text drawing here TreeNodeStates curState = e.State; Font font = (node.NodeFont != null) ? node.NodeFont : node.TreeView.Font; Color color = (((curState & TreeNodeStates.Selected) == TreeNodeStates.Selected) && node.TreeView.Focused) ? SystemColors.HighlightText : (node.ForeColor != Color.Empty) ? node.ForeColor : node.TreeView.ForeColor; // Draw the actual node. if ((curState & TreeNodeStates.Selected) == TreeNodeStates.Selected) { g.FillRectangle(SystemBrushes.Highlight, bounds); ControlPaint.DrawFocusRectangle(g, bounds, color, SystemColors.Highlight); TextRenderer.DrawText(g, e.Node.Text, font, bounds, color, TextFormatFlags.Default); } else { // For Dev10 bug 478621: We need to draw the BackColor of // nodes same as BackColor of treeview. using (Brush brush = new SolidBrush(BackColor)) { g.FillRectangle(brush, bounds); } TextRenderer.DrawText(g, e.Node.Text, font, bounds, color, TextFormatFlags.Default); } } } finally { g.Dispose(); } m.Result = (IntPtr)NativeMethods.CDRF_NOTIFYSUBITEMDRAW; return; } goto default; default: // just in case we get a spurious message, tell it to do the right thing m.Result = (IntPtr)NativeMethods.CDRF_DODEFAULT; return; } } /// /// /// Generates colors for each item. This can be overridden to provide colors on a per state/per node /// basis, rather than using the ForeColor/BackColor/NodeFont properties on TreeNode. /// /// ///protected OwnerDrawPropertyBag GetItemRenderStyles(TreeNode node, int state) { OwnerDrawPropertyBag retval = new OwnerDrawPropertyBag(); if (node == null || node.propBag == null) return retval; // we only change colors if we're displaying things normally if ((state & (NativeMethods.CDIS_SELECTED | NativeMethods.CDIS_GRAYED | NativeMethods.CDIS_HOT | NativeMethods.CDIS_DISABLED))==0) { retval.ForeColor = node.propBag.ForeColor; retval.BackColor = node.propBag.BackColor; } retval.Font = node.propBag.Font; return retval; } private unsafe bool WmShowToolTip(ref Message m) { NativeMethods.NMHDR* nmhdr = (NativeMethods.NMHDR*)m.LParam; IntPtr tooltipHandle = nmhdr->hwndFrom; NativeMethods.TV_HITTESTINFO tvhip = new NativeMethods.TV_HITTESTINFO(); Point pos = Cursor.Position; pos = PointToClientInternal(pos); tvhip.pt_x = pos.X; tvhip.pt_y = pos.Y; IntPtr hnode = UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TVM_HITTEST, 0, tvhip); if (hnode != IntPtr.Zero && ((tvhip.flags & NativeMethods.TVHT_ONITEM) != 0)) { TreeNode tn = NodeFromHandle(hnode); if (tn != null) { if (!ShowNodeToolTips) // default ToolTips { Rectangle bounds = tn.Bounds; bounds.Location = this.PointToScreen(bounds.Location); UnsafeNativeMethods.SendMessage(new HandleRef(this, tooltipHandle), NativeMethods.TTM_ADJUSTRECT, 1, ref bounds); SafeNativeMethods.SetWindowPos(new HandleRef(this, tooltipHandle), NativeMethods.HWND_TOPMOST, bounds.Left, bounds.Top, 0, 0, NativeMethods.SWP_NOACTIVATE | NativeMethods.SWP_NOSIZE | NativeMethods.SWP_NOZORDER); return true; } } } return false; } /// /// /// ///private void WmNeedText(ref Message m) { NativeMethods.TOOLTIPTEXT ttt = (NativeMethods.TOOLTIPTEXT) m.GetLParam(typeof(NativeMethods.TOOLTIPTEXT)); string tipText = controlToolTipText; NativeMethods.TV_HITTESTINFO tvhip = new NativeMethods.TV_HITTESTINFO(); Point pos = Cursor.Position; pos = PointToClientInternal(pos); tvhip.pt_x = pos.X; tvhip.pt_y = pos.Y; IntPtr hnode = UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TVM_HITTEST, 0, tvhip); if (hnode != IntPtr.Zero && ((tvhip.flags & NativeMethods.TVHT_ONITEM) != 0)) { TreeNode tn = NodeFromHandle(hnode); if (ShowNodeToolTips && tn != null && (!string.IsNullOrEmpty(tn.ToolTipText))) { tipText = tn.ToolTipText; } else if (tn != null && tn.Bounds.Right > this.Bounds.Right) { tipText = tn.Text; } else { tipText = null; } } ttt.lpszText = tipText; ttt.hinst = IntPtr.Zero; // RightToLeft reading order // if (RightToLeft == RightToLeft.Yes) { ttt.uFlags |= NativeMethods.TTF_RTLREADING; } Marshal.StructureToPtr(ttt, m.LParam, false); } private unsafe void WmNotify(ref Message m) { NativeMethods.NMHDR* nmhdr = (NativeMethods.NMHDR *)m.LParam; // Custom draw code is handled separately. // if ((nmhdr->code == NativeMethods.NM_CUSTOMDRAW)) { CustomDraw(ref m); } else { NativeMethods.NMTREEVIEW* nmtv = (NativeMethods.NMTREEVIEW*)m.LParam; switch (nmtv->nmhdr.code) { case NativeMethods.TVN_ITEMEXPANDINGA: case NativeMethods.TVN_ITEMEXPANDINGW: m.Result = TvnExpanding(nmtv); break; case NativeMethods.TVN_ITEMEXPANDEDA: case NativeMethods.TVN_ITEMEXPANDEDW: TvnExpanded(nmtv); break; case NativeMethods.TVN_SELCHANGINGA: case NativeMethods.TVN_SELCHANGINGW: m.Result = TvnSelecting(nmtv); break; case NativeMethods.TVN_SELCHANGEDA: case NativeMethods.TVN_SELCHANGEDW: TvnSelected(nmtv); break; case NativeMethods.TVN_BEGINDRAGA: case NativeMethods.TVN_BEGINDRAGW: TvnBeginDrag(MouseButtons.Left, nmtv); break; case NativeMethods.TVN_BEGINRDRAGA: case NativeMethods.TVN_BEGINRDRAGW: TvnBeginDrag(MouseButtons.Right, nmtv); break; case NativeMethods.TVN_BEGINLABELEDITA: case NativeMethods.TVN_BEGINLABELEDITW: m.Result = TvnBeginLabelEdit((NativeMethods.NMTVDISPINFO)m.GetLParam(typeof(NativeMethods.NMTVDISPINFO))); break; case NativeMethods.TVN_ENDLABELEDITA: case NativeMethods.TVN_ENDLABELEDITW: m.Result = TvnEndLabelEdit((NativeMethods.NMTVDISPINFO)m.GetLParam(typeof(NativeMethods.NMTVDISPINFO))); break; case NativeMethods.NM_CLICK: case NativeMethods.NM_RCLICK: MouseButtons button = MouseButtons.Left; NativeMethods.TV_HITTESTINFO tvhip = new NativeMethods.TV_HITTESTINFO(); Point pos = Cursor.Position; pos = PointToClientInternal(pos); tvhip.pt_x = pos.X; tvhip.pt_y = pos.Y; IntPtr hnode = UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TVM_HITTEST, 0, tvhip); if (nmtv->nmhdr.code != NativeMethods.NM_CLICK || (tvhip.flags & NativeMethods.TVHT_ONITEM) != 0) { button = nmtv->nmhdr.code == NativeMethods.NM_CLICK ? MouseButtons.Left : MouseButtons.Right; } // The treeview's WndProc doesn't get the WM_LBUTTONUP messages when // LBUTTONUP happens on TVHT_ONITEM. This is a comctl quirk. // We work around that by calling OnMouseUp here. if (nmtv->nmhdr.code != NativeMethods.NM_CLICK || (tvhip.flags & NativeMethods.TVHT_ONITEM) != 0 || FullRowSelect) { if (hnode != IntPtr.Zero && !ValidationCancelled) { OnNodeMouseClick(new TreeNodeMouseClickEventArgs(NodeFromHandle(hnode), button, 1, pos.X, pos.Y)); OnClick(new MouseEventArgs(button, 1, pos.X, pos.Y, 0)); OnMouseClick(new MouseEventArgs(button, 1, pos.X, pos.Y, 0)); } } if (nmtv->nmhdr.code == NativeMethods.NM_RCLICK) { TreeNode treeNode = NodeFromHandle(hnode); if (treeNode != null && (treeNode.ContextMenu != null || treeNode.ContextMenuStrip != null)) { ShowContextMenu(treeNode); } else { treeViewState[TREEVIEWSTATE_showTreeViewContextMenu] = true; SendMessage(NativeMethods.WM_CONTEXTMENU, Handle, SafeNativeMethods.GetMessagePos()); } m.Result = (IntPtr)1; } if (!treeViewState[TREEVIEWSTATE_mouseUpFired]) { if (nmtv->nmhdr.code != NativeMethods.NM_CLICK || (tvhip.flags & NativeMethods.TVHT_ONITEM) != 0) { // The treeview's WndProc doesn't get the WM_LBUTTONUP messages when // LBUTTONUP happens on TVHT_ONITEM. This is a comctl quirk. // We work around that by calling OnMouseUp here. OnMouseUp(new MouseEventArgs(button, 1, pos.X, pos.Y, 0)); treeViewState[TREEVIEWSTATE_mouseUpFired] = true; } } break; } } } /// /// /// Shows the context menu for the Treenode. /// ///private void ShowContextMenu(TreeNode treeNode) { if (treeNode.ContextMenu != null || treeNode.ContextMenuStrip != null) { ContextMenu contextMenu = treeNode.ContextMenu; ContextMenuStrip menu = treeNode.ContextMenuStrip; if (contextMenu != null) { NativeMethods.POINT pt = new NativeMethods.POINT(); UnsafeNativeMethods.GetCursorPos(pt); // VS7 #38994 // The solution to this problem was found in MSDN Article ID: Q135788. // Summary: the current window must be made the foreground window // before calling TrackPopupMenuEx, and a task switch must be // forced after the call. UnsafeNativeMethods.SetForegroundWindow(new HandleRef(this, this.Handle)); contextMenu.OnPopup( EventArgs.Empty ); SafeNativeMethods.TrackPopupMenuEx(new HandleRef(contextMenu, contextMenu.Handle), NativeMethods.TPM_VERTICAL, pt.x, pt.y, new HandleRef(this, this.Handle), null); // Force task switch (see above) UnsafeNativeMethods.PostMessage(new HandleRef(this, this.Handle), NativeMethods.WM_NULL, IntPtr.Zero, IntPtr.Zero); } // VsWhidbey : 432712. // Need to send TVM_SELECTITEM to highlight the node while the contextMenuStrip is being shown. else if (menu != null) { UnsafeNativeMethods.PostMessage(new HandleRef(this, Handle), NativeMethods.TVM_SELECTITEM, NativeMethods.TVGN_DROPHILITE, treeNode.Handle); menu.ShowInternal(this, PointToClient(MousePosition),/*keyboardActivated*/false); menu.Closing += new ToolStripDropDownClosingEventHandler(this.ContextMenuStripClosing); } } } // VsWhidbey : 432712. // Need to send TVM_SELECTITEM to reset the node-highlighting while the contextMenuStrip is being closed so that the treeview reselects the SelectedNode. private void ContextMenuStripClosing(object sender, ToolStripDropDownClosingEventArgs e) { ContextMenuStrip strip = sender as ContextMenuStrip; // Unhook the Event. strip.Closing -= new ToolStripDropDownClosingEventHandler(this.ContextMenuStripClosing); SendMessage(NativeMethods.TVM_SELECTITEM, NativeMethods.TVGN_DROPHILITE, null); } private void WmPrint(ref Message m) { base.WndProc(ref m); if ((NativeMethods.PRF_NONCLIENT & (int)m.LParam) != 0 && Application.RenderWithVisualStyles && this.BorderStyle == BorderStyle.Fixed3D) { IntSecurity.UnmanagedCode.Assert(); try { using (Graphics g = Graphics.FromHdc(m.WParam)) { Rectangle rect = new Rectangle(0, 0, this.Size.Width - 1, this.Size.Height - 1); g.DrawRectangle(new Pen(VisualStyleInformation.TextControlBorder), rect); rect.Inflate(-1, -1); g.DrawRectangle(SystemPens.Window, rect); } } finally { CodeAccessPermission.RevertAssert(); } } } /// /// /// ///[SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] protected override void WndProc(ref Message m) { switch (m.Msg) { case NativeMethods.WM_WINDOWPOSCHANGING: case NativeMethods.WM_NCCALCSIZE: case NativeMethods.WM_WINDOWPOSCHANGED: case NativeMethods.WM_SIZE: // While we are changing size of treeView to avoid the scrollbar; dont respond to the window-sizing messages. if (treeViewState[TREEVIEWSTATE_stopResizeWindowMsgs]) { //Debug.WriteLineIf(treeViewState[TREEVIEWSTATE_stopResizeWindowMsgs], "Sending message directly to DefWndProc() : " + m.ToString()); DefWndProc(ref m); } else { base.WndProc(ref m); } break; case NativeMethods.WM_HSCROLL: base.WndProc(ref m); if (DrawMode == TreeViewDrawMode.OwnerDrawAll) { //VsW : 432718 Invalidate(); } break; case NativeMethods.WM_PRINT: WmPrint(ref m); break; case NativeMethods.TVM_SETITEMA: case NativeMethods.TVM_SETITEMW: base.WndProc(ref m); if (this.CheckBoxes) { NativeMethods.TV_ITEM item = (NativeMethods.TV_ITEM) m.GetLParam(typeof(NativeMethods.TV_ITEM)); // Check for invalid node handle if (item.hItem != IntPtr.Zero) { NativeMethods.TV_ITEM item1 = new NativeMethods.TV_ITEM(); item1.mask = NativeMethods.TVIF_HANDLE | NativeMethods.TVIF_STATE; item1.hItem = item.hItem; item1.stateMask = NativeMethods.TVIS_STATEIMAGEMASK; UnsafeNativeMethods.SendMessage(new HandleRef(null, this.Handle), NativeMethods.TVM_GETITEM, 0, ref item1); TreeNode node = NodeFromHandle(item.hItem); node.CheckedStateInternal = ((item1.state >> 12) > 1); } } break; case NativeMethods.WM_NOTIFY: NativeMethods.NMHDR nmhdr = (NativeMethods.NMHDR) m.GetLParam(typeof(NativeMethods.NMHDR)); switch (nmhdr.code) { case NativeMethods.TTN_GETDISPINFOA: case NativeMethods.TTN_GETDISPINFOW: // MSDN: // Setting the max width has the added benefit of enabling multiline // tool tips! // UnsafeNativeMethods.SendMessage(new HandleRef(nmhdr, nmhdr.hwndFrom), NativeMethods.TTM_SETMAXTIPWIDTH, 0, SystemInformation.MaxWindowTrackSize.Width); WmNeedText(ref m); m.Result = (IntPtr)1; return; case NativeMethods.TTN_SHOW: if (WmShowToolTip(ref m)) { m.Result = (IntPtr)1; return; } else { base.WndProc(ref m); break; } default: base.WndProc(ref m); break; } break; case NativeMethods.WM_REFLECT + NativeMethods.WM_NOTIFY: WmNotify(ref m); break; case NativeMethods.WM_LBUTTONDBLCLK: WmMouseDown(ref m, MouseButtons.Left, 2); //just maintain state and fire double click.. in final mouseUp... treeViewState[TREEVIEWSTATE_doubleclickFired] = true; //fire Up in the Wndproc !! treeViewState[TREEVIEWSTATE_mouseUpFired] = false; //problem getting the UP... outside the control... // CaptureInternal = true; break; case NativeMethods.WM_LBUTTONDOWN: try { treeViewState[TREEVIEWSTATE_ignoreSelects] = true; FocusInternal(); } finally { treeViewState[ TREEVIEWSTATE_ignoreSelects] = false; } //Always Reset the MouseupFired.... treeViewState[TREEVIEWSTATE_mouseUpFired] = false; NativeMethods.TV_HITTESTINFO tvhip = new NativeMethods.TV_HITTESTINFO(); tvhip.pt_x = NativeMethods.Util.SignedLOWORD(m.LParam); tvhip.pt_y = NativeMethods.Util.SignedHIWORD(m.LParam); hNodeMouseDown = UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TVM_HITTEST, 0, tvhip); // This gets around the TreeView behavior of temporarily moving the selection // highlight to a node when the user clicks on its checkbox. if ((tvhip.flags & NativeMethods.TVHT_ONITEMSTATEICON) != 0) { //We donot pass the Message to the Control .. so fire MouseDowm ... OnMouseDown(new MouseEventArgs(MouseButtons.Left, 1, NativeMethods.Util.SignedLOWORD(m.LParam), NativeMethods.Util.SignedHIWORD(m.LParam), 0)); if (!ValidationCancelled && CheckBoxes) { TreeNode node = NodeFromHandle(hNodeMouseDown); bool eventReturn = TreeViewBeforeCheck(node, TreeViewAction.ByMouse); if (!eventReturn && node != null) { node.CheckedInternal = !node.CheckedInternal; TreeViewAfterCheck(node, TreeViewAction.ByMouse); } } m.Result = IntPtr.Zero; } else { WmMouseDown(ref m, MouseButtons.Left, 1); } downButton = MouseButtons.Left; break; case NativeMethods.WM_LBUTTONUP: case NativeMethods.WM_RBUTTONUP: NativeMethods.TV_HITTESTINFO tvhi = new NativeMethods.TV_HITTESTINFO(); tvhi.pt_x = NativeMethods.Util.SignedLOWORD(m.LParam); tvhi.pt_y = NativeMethods.Util.SignedHIWORD(m.LParam); IntPtr hnode = UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TVM_HITTEST, 0, tvhi); //Important for CheckBoxes ... click needs to be fired ... // if(hnode != IntPtr.Zero) { if (!ValidationCancelled && !treeViewState[TREEVIEWSTATE_doubleclickFired] & !treeViewState[TREEVIEWSTATE_mouseUpFired]) { //OnClick(EventArgs.Empty); //If the hit-tested node here is the same as the node we hit-tested //on mouse down then we will fire our OnNodeMoseClick event. if (hnode == hNodeMouseDown) { OnNodeMouseClick(new TreeNodeMouseClickEventArgs(NodeFromHandle(hnode), downButton, 1, NativeMethods.Util.SignedLOWORD(m.LParam), NativeMethods.Util.SignedHIWORD(m.LParam))); } OnClick(new MouseEventArgs(downButton, 1, NativeMethods.Util.SignedLOWORD(m.LParam), NativeMethods.Util.SignedHIWORD(m.LParam), 0)); OnMouseClick(new MouseEventArgs(downButton, 1, NativeMethods.Util.SignedLOWORD(m.LParam), NativeMethods.Util.SignedHIWORD(m.LParam), 0)); } if (treeViewState[TREEVIEWSTATE_doubleclickFired]) { treeViewState[TREEVIEWSTATE_doubleclickFired] = false; if (!ValidationCancelled) { //OnDoubleClick(EventArgs.Empty); OnNodeMouseDoubleClick(new TreeNodeMouseClickEventArgs(NodeFromHandle(hnode), downButton, 2, NativeMethods.Util.SignedLOWORD(m.LParam), NativeMethods.Util.SignedHIWORD(m.LParam))); OnDoubleClick(new MouseEventArgs(downButton, 2, NativeMethods.Util.SignedLOWORD(m.LParam), NativeMethods.Util.SignedHIWORD(m.LParam), 0)); OnMouseDoubleClick(new MouseEventArgs(downButton, 2, NativeMethods.Util.SignedLOWORD(m.LParam), NativeMethods.Util.SignedHIWORD(m.LParam), 0)); } } } if (!treeViewState[TREEVIEWSTATE_mouseUpFired]) OnMouseUp(new MouseEventArgs(downButton, 1, NativeMethods.Util.SignedLOWORD(m.LParam), NativeMethods.Util.SignedHIWORD(m.LParam), 0)); treeViewState[TREEVIEWSTATE_doubleclickFired] = false; treeViewState[TREEVIEWSTATE_mouseUpFired] = false; CaptureInternal = false; //always clear our hit-tested node we cached on mouse down hNodeMouseDown = IntPtr.Zero; break; case NativeMethods.WM_MBUTTONDBLCLK: //fire Up in the Wndproc !! treeViewState[TREEVIEWSTATE_mouseUpFired] = false; WmMouseDown(ref m, MouseButtons.Middle, 2); break; case NativeMethods.WM_MBUTTONDOWN: //Always Reset the MouseupFired.... treeViewState[TREEVIEWSTATE_mouseUpFired] = false; WmMouseDown(ref m, MouseButtons.Middle, 1); downButton = MouseButtons.Middle; break; case NativeMethods.WM_MOUSELEAVE: // if the mouse leaves and then reenters the TreeView // NodeHovered events should be raised. prevHoveredNode = null; base.WndProc(ref m); break; case NativeMethods.WM_RBUTTONDBLCLK: WmMouseDown(ref m, MouseButtons.Right, 2); //just maintain state and fire double click.. in final mouseUp... treeViewState[TREEVIEWSTATE_doubleclickFired] = true; //fire Up in the Wndproc !! treeViewState[TREEVIEWSTATE_mouseUpFired] = false; //problem getting the UP... outside the control... // CaptureInternal = true; break; case NativeMethods.WM_RBUTTONDOWN: //Always Reset the MouseupFired.... treeViewState[TREEVIEWSTATE_mouseUpFired] = false; //Cache the hit-tested node for verification when mouse up is fired NativeMethods.TV_HITTESTINFO tvhit = new NativeMethods.TV_HITTESTINFO(); tvhit.pt_x = NativeMethods.Util.SignedLOWORD(m.LParam); tvhit.pt_y = NativeMethods.Util.SignedHIWORD(m.LParam); hNodeMouseDown = UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TVM_HITTEST, 0, tvhit); WmMouseDown(ref m, MouseButtons.Right, 1); downButton = MouseButtons.Right; break; //# VS7 15052 case NativeMethods.WM_SYSCOLORCHANGE: SendMessage(NativeMethods.TVM_SETINDENT, Indent, 0); base.WndProc(ref m); break; case NativeMethods.WM_SETFOCUS: // If we get focus through the LBUttonDown .. we might have done the validation... // so skip it.. if (treeViewState[TREEVIEWSTATE_lastControlValidated]) { treeViewState[TREEVIEWSTATE_lastControlValidated] = false; WmImeSetFocus(); DefWndProc(ref m); OnGotFocus(EventArgs.Empty); } else { base.WndProc(ref m); } break; case NativeMethods.WM_CONTEXTMENU: if (treeViewState[TREEVIEWSTATE_showTreeViewContextMenu]) { treeViewState[TREEVIEWSTATE_showTreeViewContextMenu] = false; base.WndProc(ref m); } else { // this is the Shift + F10 Case.... TreeNode treeNode = SelectedNode; if (treeNode != null && (treeNode.ContextMenu != null || treeNode.ContextMenuStrip !=null)) { Point client; client = new Point(treeNode.Bounds.X , treeNode.Bounds.Y + treeNode.Bounds.Height / 2); // VisualStudio7 # 156, only show the context menu when clicked in the client area if (ClientRectangle.Contains( client )) { if (treeNode.ContextMenu != null) { treeNode.ContextMenu.Show(this, client); } else if (treeNode.ContextMenuStrip !=null) { bool keyboardActivated = (unchecked((int)(long)m.LParam) == -1); treeNode.ContextMenuStrip.ShowInternal(this, client, keyboardActivated); } } } else { // in this case we dont have a selected node. The base // will ensure we're constrained to the client area. base.WndProc (ref m); } } break; default: base.WndProc(ref m); break; } } } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007.
Link Menu

This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- InsufficientMemoryException.cs
- WindowsGraphics2.cs
- AutoResizedEvent.cs
- KnowledgeBase.cs
- cookiecollection.cs
- TcpConnectionPool.cs
- ContentElementCollection.cs
- CompilationRelaxations.cs
- FontNamesConverter.cs
- ThreadExceptionEvent.cs
- _NetRes.cs
- DynamicPropertyHolder.cs
- CompressionTransform.cs
- FixedLineResult.cs
- XamlReader.cs
- QilChoice.cs
- CommentEmitter.cs
- __Filters.cs
- DataGridrowEditEndingEventArgs.cs
- TextSelectionHelper.cs
- OverflowException.cs
- TextParagraph.cs
- CodeComment.cs
- categoryentry.cs
- sqlstateclientmanager.cs
- Point3D.cs
- ManagementClass.cs
- FormatException.cs
- LongTypeConverter.cs
- TextSelectionHighlightLayer.cs
- SaveFileDialog.cs
- PointAnimationUsingPath.cs
- nulltextcontainer.cs
- BidPrivateBase.cs
- Stacktrace.cs
- CompModSwitches.cs
- GraphicsContainer.cs
- FileRecordSequenceCompletedAsyncResult.cs
- ScriptDescriptor.cs
- RadioButton.cs
- XmlWrappingWriter.cs
- QueryableDataSourceHelper.cs
- RubberbandSelector.cs
- XXXInfos.cs
- SizeF.cs
- ComponentSerializationService.cs
- GradientStop.cs
- OdbcFactory.cs
- RequestCacheManager.cs
- GlyphsSerializer.cs
- HorizontalAlignConverter.cs
- SqlClientFactory.cs
- PropertyEmitter.cs
- DBAsyncResult.cs
- ListMarkerSourceInfo.cs
- PropertyChangeTracker.cs
- OciLobLocator.cs
- WinCategoryAttribute.cs
- ResourceType.cs
- XDeferredAxisSource.cs
- XmlWriterTraceListener.cs
- SparseMemoryStream.cs
- NotifyIcon.cs
- TableStyle.cs
- NameNode.cs
- PermissionRequestEvidence.cs
- CompilerGlobalScopeAttribute.cs
- StateElementCollection.cs
- ContextMenuService.cs
- NodeLabelEditEvent.cs
- OdbcFactory.cs
- ByteAnimationUsingKeyFrames.cs
- DirectionalAction.cs
- KeyEventArgs.cs
- MenuTracker.cs
- EdmValidator.cs
- DetailsViewInsertedEventArgs.cs
- SqlExpressionNullability.cs
- Identifier.cs
- CurrentChangingEventArgs.cs
- BoundColumn.cs
- ParallelTimeline.cs
- TailCallAnalyzer.cs
- ResourceAssociationSet.cs
- CodeDirectiveCollection.cs
- XmlByteStreamWriter.cs
- SecurityUtils.cs
- FileNameEditor.cs
- Normalization.cs
- RequestTimeoutManager.cs
- HttpCacheVary.cs
- RequestCachingSection.cs
- DateTimeConverter.cs
- ByteKeyFrameCollection.cs
- Models.cs
- DataQuery.cs
- BindUriHelper.cs
- DataServiceContext.cs
- DataService.cs
- CodeTypeParameterCollection.cs