|
//------------------------------------------------------------------------------
// <copyright file="TreeView.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
//------------------------------------------------------------------------------
/*
*/
namespace System.Windows.Forms {
using System.Runtime.Remoting;
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Contracts;
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;
/// <include file='doc\TreeView.uex' path='docs/doc[@for="TreeView"]/*' />
/// <devdoc>
/// <para>
/// 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.
///
/// </para>
/// </devdoc>
[
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 const int MaxIndent = 32000; // Maximum allowable TreeView indent
private const 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
private static bool isScalingInitialized = false;
private static Size? scaledStateImageSize = null;
private static Size? ScaledStateImageSize {
get {
if (!isScalingInitialized) {
if (DpiHelper.IsScalingRequired) {
scaledStateImageSize = DpiHelper.LogicalToDeviceUnits(new Size(16, 16));
}
isScalingInitialized = true;
}
return scaledStateImageSize;
}
}
/// <include file='doc\TreeView.uex' path='docs/doc[@for="TreeView.ImageIndexer"]/*' />
/// <internalonly/>
internal ImageList.Indexer ImageIndexer {
get {
if (imageIndexer == null) {
imageIndexer = new ImageList.Indexer();
}
imageIndexer.ImageList = ImageList;
return imageIndexer;
}
}
/// <include file='doc\TreeView.uex' path='docs/doc[@for="TreeView.SelectedImageIndexer"]/*' />
/// <internalonly/>
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;
/// <include file='doc\TreeView.uex' path='docs/doc[@for="TreeView.TreeView"]/*' />
/// <devdoc>
/// Creates a TreeView control
/// </devdoc>
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);
}
/// <include file='doc\TreeView.uex' path='docs/doc[@for="TreeView.BackColor"]/*' />
/// <devdoc>
/// The background color for this control. Specifying null for
/// this parameter sets the
/// control's background color to its parent's background color.
/// </devdoc>
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);
}
}
}
/// <include file='doc\TreeView.uex' path='docs/doc[@for="TreeView.BackgroundImage"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
/// <internalonly/>
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
public override Image BackgroundImage {
get {
return base.BackgroundImage;
}
set {
base.BackgroundImage = value;
}
}
/// <include file='doc\TreeView.uex' path='docs/doc[@for="TreeView.BackgroundImageChanged"]/*' />
/// <internalonly/>
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
new public event EventHandler BackgroundImageChanged {
add {
base.BackgroundImageChanged += value;
}
remove {
base.BackgroundImageChanged -= value;
}
}
/// <include file='doc\TreeView.uex' path='docs/doc[@for="TreeView.BackgroundImageLayout"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
/// <internalonly/>
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
public override ImageLayout BackgroundImageLayout {
get {
return base.BackgroundImageLayout;
}
set {
base.BackgroundImageLayout = value;
}
}
/// <include file='doc\TreeView.uex' path='docs/doc[@for="TreeView.BackgroundImageLayoutChanged"]/*' />
/// <internalonly/>
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
new public event EventHandler BackgroundImageLayoutChanged {
add {
base.BackgroundImageLayoutChanged += value;
}
remove {
base.BackgroundImageLayoutChanged -= value;
}
}
/// <include file='doc\TreeView.uex' path='docs/doc[@for="TreeView.BorderStyle"]/*' />
/// <devdoc>
/// The border style of the window.
/// </devdoc>
[
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();
}
}
}
/// <include file='doc\TreeView.uex' path='docs/doc[@for="TreeView.CheckBoxes"]/*' />
/// <devdoc>
/// The value of the CheckBoxes property. The CheckBoxes
/// property determines if check boxes are shown next to node in the
/// tree view.
/// </devdoc>
[
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) {
if (CheckBoxes) {
UpdateStyles();
} else {
// Going from true to false requires recreation
// 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();
}
}
}
}
}
/// <include file='doc\TreeView.uex' path='docs/doc[@for="TreeView.CreateParams"]/*' />
/// <devdoc>
/// </devdoc>
/// <internalonly/>
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;
}
}
/// <include file='doc\TreeView.uex' path='docs/doc[@for="TreeView.DefaultSize"]/*' />
/// <devdoc>
/// 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.
/// </devdoc>
protected override Size DefaultSize {
get {
return new Size(121, 97);
}
}
/// <include file='doc\TreeView.uex' path='docs/doc[@for="TreeView.DoubleBuffered"]/*' />
/// <devdoc>
/// This property is overridden and hidden from statement completion
/// on controls that are based on Win32 Native Controls.
/// </devdoc>
[EditorBrowsable(EditorBrowsableState.Never)]
protected override bool DoubleBuffered {
get {
return base.DoubleBuffered;
}
set {
base.DoubleBuffered = value;
}
}
/// <include file='doc\TreeView.uex' path='docs/doc[@for="TreeView.ForeColor"]/*' />
/// <devdoc>
/// The current foreground color for this control, which is the
/// color the control uses to draw its text.
/// </devdoc>
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));
}
}
/// <include file='doc\TreeView.uex' path='docs/doc[@for="TreeView.FullRowSelect"]/*' />
/// <devdoc>
/// Determines whether the selection highlight spans across the width of the TreeView.
/// This property will have no effect if ShowLines is true.
/// </devdoc>
[
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();
}
}
}
}
/// <include file='doc\TreeView.uex' path='docs/doc[@for="TreeView.HideSelection"]/*' />
/// <devdoc>
/// The HideSelection property specifies whether the selected node will
/// be highlighted even when the TreeView loses focus.
/// </devdoc>
[
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();
}
}
}
}
/// <include file='doc\TreeView.uex' path='docs/doc[@for="TreeView.HotTracking"]/*' />
/// <devdoc>
/// The value of the HotTracking property. The HotTracking
/// property determines if nodes are highlighted as the mousepointer
/// passes over them.
/// </devdoc>
[
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();
}
}
}
}
/// <include file='doc\TreeView.uex' path='docs/doc[@for="TreeView.ImageIndex"]/*' />
/// <devdoc>
/// The default image index for nodes in the tree view.
/// </devdoc>
[
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();
}
}
}
}
/// <include file='doc\TreeView.uex' path='docs/doc[@for="TreeView.ImageKey"]/*' />
/// <devdoc>
/// The default image index for nodes in the tree view.
/// </devdoc>
[
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();
}
}
}
}
/// <include file='doc\TreeView.uex' path='docs/doc[@for="TreeView.ImageList"]/*' />
/// <devdoc>
/// Returns the image list control that is bound to the tree view.
/// </devdoc>
[
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) {
SetStateImageList(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);
}
}
/// <include file='doc\TreeView.uex' path='docs/doc[@for="TreeView.StateImageList"]/*' />
/// <devdoc>
/// Returns the state image list control that is bound to the tree view.
/// </devdoc>
[
SRCategory(SR.CatBehavior),
DefaultValue(null),
SRDescription(SR.TreeViewStateImageListDescr)
]
public ImageList StateImageList {
get {
return stateImageList;
}
set {
if (value != stateImageList) {
DetachStateImageListHandlers();
stateImageList = value;
AttachStateImageListHandlers();
// Update TreeView's images
//
if (IsHandleCreated)
{
UpdateNativeStateImageList();
// 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();
}
}
}
}
}
/// <include file='doc\TreeView.uex' path='docs/doc[@for="TreeView.Indent"]/*' />
/// <devdoc>
/// The indentation level in pixels.
/// </devdoc>
[
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));
}
}
}
}
/// <include file='doc\TreeView.uex' path='docs/doc[@for="TreeView.ItemHeight"]/*' />
/// <devdoc>
/// The height of every item in the tree view, in pixels.
/// </devdoc>
[
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));
}
}
}
}
/// <include file='doc\TreeView.uex' path='docs/doc[@for="TreeView.LabelEdit"]/*' />
/// <devdoc>
/// The LabelEdit property determines if the label text
/// of nodes in the tree view is editable.
/// </devdoc>
[
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();
}
}
}
}
/// <include file='doc\TreeView.uex' path='docs/doc[@for="TreeView.LineColor"]/*' />
/// <devdoc>
/// This is the color of the lines that connect the nodes of the Treeview.
/// </devdoc>
[
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));
}
}
}
}
/// <include file='doc\TreeView.uex' path='docs/doc[@for="TreeView.Nodes"]/*' />
/// <devdoc>
/// The collection of nodes associated with this TreeView control
/// </devdoc>
[
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;
}
}
/// <include file='doc\TreeView.uex' path='docs/doc[@for="TreeView.DrawMode"]/*' />
/// <devdoc>
/// <para>
/// Indicates the drawing mode for the tree view.
/// </para>
/// </devdoc>
[
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);
}
}
}
}
/// <include file='doc\TreeView.uex' path='docs/doc[@for="TreeView.PathSeparator"]/*' />
/// <devdoc>
/// The delimeter string used by TreeNode.getFullPath().
/// </devdoc>
[
SRCategory(SR.CatBehavior),
DefaultValue("\\"),
SRDescription(SR.TreeViewPathSeparatorDescr)
]
public string PathSeparator {
get {
return pathSeparator;
}
set {
pathSeparator = value;
}
}
/// <include file='doc\TreeView.uex' path='docs/doc[@for="TreeView.Padding"]/*' />
/// <devdoc>
/// <para>
/// <para>[To be supplied.]</para>
/// </para>
/// </devdoc>
[
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; }
}
/// <include file='doc\TreeView.uex' path='docs/doc[@for="TreeView.RightToLeftLayout"]/*' />
/// <devdoc>
/// 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.
/// </devdoc>
[
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);
}
}
}
}
/// <include file='doc\TreeView.uex' path='docs/doc[@for="TreeView.Scrollable"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
[
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();
}
}
}
/// <include file='doc\TreeView.uex' path='docs/doc[@for="TreeView.SelectedImageIndex"]/*' />
/// <devdoc>
/// The image index that a node will display when selected.
/// The index applies to the ImageList referred to by the imageList property,
/// </devdoc>
[
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();
}
}
}
}
/// <include file='doc\TreeView.uex' path='docs/doc[@for="TreeView.SelectedImageKey"]/*' />
/// <devdoc>
/// The default image index for nodes in the tree view.
/// </devdoc>
[
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();
}
}
}
}
/// <include file='doc\TreeView.uex' path='docs/doc[@for="TreeView.SelectedNode"]/*' />
/// <devdoc>
/// The currently selected tree node, or null if nothing is selected.
/// </devdoc>
[
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;
}
}
}
/// <include file='doc\TreeView.uex' path='docs/doc[@for="TreeView.ShowLines"]/*' />
/// <devdoc>
/// The ShowLines property determines if lines are drawn between
/// nodes in the tree view.
/// </devdoc>
[
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();
}
}
}
}
/// <include file='doc\TreeView.uex' path='docs/doc[@for="TreeView.ShowNodeToolTips"]/*' />
/// <devdoc>
/// The ShowLines property determines whether or not the tooltips willbe displayed on the nodes
/// </devdoc>
[
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();
}
}
}
/// <include file='doc\TreeView.uex' path='docs/doc[@for="TreeView.ShowPlusMinus"]/*' />
/// <devdoc>
/// The ShowPlusMinus property determines if the "plus/minus"
/// expand button is shown next to tree nodes that have children.
/// </devdoc>
[
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();
}
}
}
}
/// <include file='doc\TreeView.uex' path='docs/doc[@for="TreeView.ShowRootLines"]/*' />
/// <devdoc>
/// Determines if lines are draw between nodes at the root of
/// the tree view.
/// </devdoc>
[
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();
}
}
}
}
/// <include file='doc\TreeView.uex' path='docs/doc[@for="TreeView.Sorted"]/*' />
/// <devdoc>
/// The Sorted property determines if nodes in the tree view are sorted.
/// </devdoc>
[
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();
}
}
}
}
/// <include file='doc\TreeView.uex' path='docs/doc[@for="TreeView.TreeViewItemSorter"]/*' />
/// <devdoc>
/// The sorting comparer for this TreeView.
/// </devdoc>
[
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();
}
}
}
}
/// <include file='doc\TreeView.uex' path='docs/doc[@for="TreeView.Text"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never), Bindable(false)]
public override string Text {
get {
return base.Text;
}
set {
base.Text = value;
}
}
/// <include file='doc\TreeView.uex' path='docs/doc[@for="TreeView.TextChanged"]/*' />
/// <internalonly/>
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
new public event EventHandler TextChanged {
add {
base.TextChanged += value;
}
remove {
base.TextChanged -= value;
}
}
/// <include file='doc\TreeView.uex' path='docs/doc[@for="TreeView.TopNode"]/*' />
/// <devdoc>
/// 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.
/// </devdoc>
[
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;
}
}
}
/// <include file='doc\TreeView.uex' path='docs/doc[@for="TreeView.VisibleCount"]/*' />
/// <devdoc>
/// 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
/// </devdoc>
[
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;
}
}
/// <include file='doc\TreeView.uex' path='docs/doc[@for="TreeView.BeforeLabelEdit"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
[SRCategory(SR.CatBehavior), SRDescription(SR.TreeViewBeforeEditDescr)]
public event NodeLabelEditEventHandler BeforeLabelEdit {
add {
onBeforeLabelEdit += value;
}
remove {
onBeforeLabelEdit -= value;
}
}
/// <include file='doc\TreeView.uex' path='docs/doc[@for="TreeView.AfterLabelEdit"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
[SRCategory(SR.CatBehavior), SRDescription(SR.TreeViewAfterEditDescr)]
public event NodeLabelEditEventHandler AfterLabelEdit {
add {
onAfterLabelEdit += value;
}
remove {
onAfterLabelEdit -= value;
}
}
/// <include file='doc\TreeView.uex' path='docs/doc[@for="TreeView.BeforeCheck"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
[SRCategory(SR.CatBehavior), SRDescription(SR.TreeViewBeforeCheckDescr)]
public event TreeViewCancelEventHandler BeforeCheck {
add {
onBeforeCheck += value;
}
remove {
onBeforeCheck -= value;
}
}
/// <include file='doc\TreeView.uex' path='docs/doc[@for="TreeView.AfterCheck"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
[SRCategory(SR.CatBehavior), SRDescription(SR.TreeViewAfterCheckDescr)]
public event TreeViewEventHandler AfterCheck {
add {
onAfterCheck += value;
}
remove {
onAfterCheck -= value;
}
}
/// <include file='doc\TreeView.uex' path='docs/doc[@for="TreeView.BeforeCollapse"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
[SRCategory(SR.CatBehavior), SRDescription(SR.TreeViewBeforeCollapseDescr)]
public event TreeViewCancelEventHandler BeforeCollapse {
add {
onBeforeCollapse += value;
}
remove {
onBeforeCollapse -= value;
}
}
/// <include file='doc\TreeView.uex' path='docs/doc[@for="TreeView.AfterCollapse"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
[SRCategory(SR.CatBehavior), SRDescription(SR.TreeViewAfterCollapseDescr)]
public event TreeViewEventHandler AfterCollapse {
add {
onAfterCollapse += value;
}
remove {
onAfterCollapse -= value;
}
}
/// <include file='doc\TreeView.uex' path='docs/doc[@for="TreeView.BeforeExpand"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
[SRCategory(SR.CatBehavior), SRDescription(SR.TreeViewBeforeExpandDescr)]
public event TreeViewCancelEventHandler BeforeExpand {
add {
onBeforeExpand += value;
}
remove {
onBeforeExpand -= value;
}
}
/// <include file='doc\TreeView.uex' path='docs/doc[@for="TreeView.AfterExpand"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
[SRCategory(SR.CatBehavior), SRDescription(SR.TreeViewAfterExpandDescr)]
public event TreeViewEventHandler AfterExpand {
add {
onAfterExpand += value;
}
remove {
onAfterExpand -= value;
}
}
/// <include file='doc\TreeView.uex' path='docs/doc[@for="TreeView.DrawNode"]/*' />
/// <devdoc>
/// <para>Fires when a TreeView node needs to be drawn.</para>
/// </devdoc>
[SRCategory(SR.CatBehavior), SRDescription(SR.TreeViewDrawNodeEventDescr)]
public event DrawTreeNodeEventHandler DrawNode {
add {
onDrawNode += value;
}
remove {
onDrawNode -= value;
}
}
/// <include file='doc\TreeView.uex' path='docs/doc[@for="TreeView.ItemDrag"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
[SRCategory(SR.CatAction), SRDescription(SR.ListViewItemDragDescr)]
public event ItemDragEventHandler ItemDrag {
add {
onItemDrag += value;
}
remove {
onItemDrag -= value;
}
}
/// <include file='doc\TreeView.uex' path='docs/doc[@for="TreeView.NodeMouseHover"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
[SRCategory(SR.CatAction), SRDescription(SR.TreeViewNodeMouseHoverDescr)]
public event TreeNodeMouseHoverEventHandler NodeMouseHover {
add {
onNodeMouseHover += value;
}
remove {
onNodeMouseHover -= value;
}
}
/// <include file='doc\TreeView.uex' path='docs/doc[@for="TreeView.BeforeSelect"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
[SRCategory(SR.CatBehavior), SRDescription(SR.TreeViewBeforeSelectDescr)]
public event TreeViewCancelEventHandler BeforeSelect {
add {
onBeforeSelect += value;
}
remove {
onBeforeSelect -= value;
}
}
/// <include file='doc\TreeView.uex' path='docs/doc[@for="TreeView.AfterSelect"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
[SRCategory(SR.CatBehavior), SRDescription(SR.TreeViewAfterSelectDescr)]
public event TreeViewEventHandler AfterSelect {
add {
onAfterSelect += value;
}
remove {
onAfterSelect -= value;
}
}
/// <include file='doc\TreeView.uex' path='docs/doc[@for="TreeView.OnPaint"]/*' />
/// <devdoc>
/// TreeView Onpaint.
/// </devdoc>
/// <internalonly/><hideinheritance/>
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
public new event PaintEventHandler Paint {
add {
base.Paint += value;
}
remove {
base.Paint -= value;
}
}
/// <include file='doc\TreeView.uex' path='docs/doc[@for="TreeView.NodeMouseClick"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
[SRCategory(SR.CatBehavior), SRDescription(SR.TreeViewNodeMouseClickDescr)]
public event TreeNodeMouseClickEventHandler NodeMouseClick {
add {
onNodeMouseClick += value;
}
remove {
onNodeMouseClick -= value;
}
}
/// <include file='doc\TreeView.uex' path='docs/doc[@for="TreeView.NodeMouseDoubleClick"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
[SRCategory(SR.CatBehavior), SRDescription(SR.TreeViewNodeMouseDoubleClickDescr)]
public event TreeNodeMouseClickEventHandler NodeMouseDoubleClick {
add {
onNodeMouseDoubleClick += value;
}
remove {
onNodeMouseDoubleClick -= value;
}
}
/// <include file='doc\TreeView.uex' path='docs/doc[@for="TreeView.RightToLeftLayoutChanged"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
[SRCategory(SR.CatPropertyChanged), SRDescription(SR.ControlOnRightToLeftLayoutChangedDescr)]
public event EventHandler RightToLeftLayoutChanged {
add {
onRightToLeftLayoutChanged += value;
}
remove {
onRightToLeftLayoutChanged -= value;
}
}
/// <include file='doc\TreeView.uex' path='docs/doc[@for="TreeView.BeginUpdate"]/*' />
/// <devdoc>
/// 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().
/// </devdoc>
public void BeginUpdate() {
BeginUpdateInternal();
}
/// <include file='doc\TreeView.uex' path='docs/doc[@for="TreeView.CollapseAll"]/*' />
/// <devdoc>
/// Collapses all nodes at the root level.
/// </devdoc>
public void CollapseAll() {
root.Collapse();
}
/// <include file='doc\TreeView.uex' path='docs/doc[@for="TreeView.CreateHandle"]/*' />
/// <devdoc>
/// </devdoc>
/// <internalonly/>
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();
}
/// <include file='doc\TreeView.uex' path='docs/doc[@for="TreeView.DetachImageList"]/*' />
/// <devdoc>
/// 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.
/// </devdoc>
/// <internalonly/>
private void DetachImageList(object sender, EventArgs e) {
ImageList = null;
}
/// <include file='doc\TreeView.uex' path='docs/doc[@for="TreeView.DetachStateImageList"]/*' />
/// <devdoc>
/// 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.
/// </devdoc>
/// <internalonly/>
private void DetachStateImageList(object sender, EventArgs e) {
internalStateImageList = null;
StateImageList = null;
}
/// <include file='doc\TreeView.uex' path='docs/doc[@for="TreeView.Dispose"]/*' />
/// <devdoc>
/// </devdoc>
/// <internalonly/>
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);
}
/// <include file='doc\TreeView.uex' path='docs/doc[@for="TreeView.EndUpdate"]/*' />
/// <devdoc>
/// 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().
/// </devdoc>
public void EndUpdate() {
EndUpdateInternal();
}
/// <include file='doc\TreeView.uex' path='docs/doc[@for="TreeView.ExpandAll"]/*' />
/// <devdoc>
/// Expands all nodes at the root level.
/// </devdoc>
public void ExpandAll() {
root.ExpandAll();
}
/// <include file='doc\TreeView.uex' path='docs/doc[@for="TreeView.ForceScrollbarUpdate"]/*' />
/// <devdoc>
/// Forces the TreeView to recalculate all its nodes widths so that it updates the
/// scrollbars as appropriate.
/// </devdoc>
/// <internalonly/>
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);
}
}
}
/// <include file='doc\TreeView.uex' path='docs/doc[@for="TreeView.SetToolTip"]/*' />
/// <devdoc>
/// Called by ToolTip to poke in that Tooltip into this ComCtl so that the Native ChildToolTip is not exposed.
/// </devdoc>
/// <internalonly/>
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;
}
}
/// <include file='doc\TreeView.uex' path='docs/doc[@for="TreeView.HitTest"]/*' />
/// <devdoc>
/// Gives the information about which part of the treeNode is at the given point.
/// </devdoc>
public TreeViewHitTestInfo HitTest(Point pt) {
return HitTest(pt.X, pt.Y);
}
/// <include file='doc\TreeView.uex' path='docs/doc[@for="TreeView.HitTest"]/*' />
/// <devdoc>
/// Gives the information about which part of the treeNode is at the given x, y.
/// </devdoc>
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));
}
/// <include file='doc\TreeView.uex' path='docs/doc[@for="TreeView.TreeViewBeforeAfterCheck"]/*' />
/// <devdoc>
/// Defined so that a tree node can use it
///
/// </devdoc>
/// <internalonly/>
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));
}
/// <include file='doc\TreeView.uex' path='docs/doc[@for="TreeView.GetNodeCount"]/*' />
/// <devdoc>
/// Returns count of nodes at root, optionally including all subtrees.
/// </devdoc>
public int GetNodeCount(bool includeSubTrees) {
return root.GetNodeCount(includeSubTrees);
}
/// <include file='doc\TreeView.uex' path='docs/doc[@for="TreeView.GetNodeAt"]/*' />
/// <devdoc>
/// Returns the TreeNode at the given location in tree view coordinates.
/// </devdoc>
public TreeNode GetNodeAt(Point pt) {
return GetNodeAt(pt.X, pt.Y);
}
/// <include file='doc\TreeView.uex' path='docs/doc[@for="TreeView.GetNodeAt1"]/*' />
/// <devdoc>
/// Returns the TreeNode at the given location in tree view coordinates.
/// </devdoc>
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;
}
SetStateImageList(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)
{
if (ScaledStateImageSize != null)
{
internalStateImageList.ImageSize = (Size)ScaledStateImageSize;
}
SetStateImageList(internalStateImageList.Handle);
}
}
else //stateImageList == null || stateImageList.Images.Count = 0;
{
UpdateCheckedState(root, true);
}
}
}
/// <include file='doc\TreeView.uex' path='docs/doc[@for="TreeView.IsInputKey"]/*' />
/// <internalonly/>
/// <devdoc>
/// <para>
/// Overridden to handle RETURN key.
/// </para>
/// </devdoc>
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);
}
/// <include file='doc\TreeView.uex' path='docs/doc[@for="TreeView.NodeFromHandle"]/*' />
/// <devdoc>
/// Note this can be null - particularly if any windows messages get generated during
/// the insertion of a tree node (TVM_INSERTITEM)
/// </devdoc>
/// <internalonly/>
internal TreeNode NodeFromHandle(IntPtr handle) {
TreeNode node = (TreeNode)nodeTable[handle];
return node;
}
/// <include file='doc\TreeView.uex' path='docs/doc[@for="TreeView.OnDrawNode"]/*' />
/// <devdoc>
/// Fires the DrawNode event.
/// </devdoc>
protected virtual void OnDrawNode(DrawTreeNodeEventArgs e) {
if (onDrawNode != null) onDrawNode(this, e);
}
/// <include file='doc\TreeView.uex' path='docs/doc[@for="TreeView.OnHandleCreated"]/*' />
/// <devdoc>
/// </devdoc>
/// <internalonly/>
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)
{
UpdateNativeStateImageList();
}
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;
}
// Replace the native control's ImageList with our current stateImageList
// set the value of internalStateImageList to the new list
private void UpdateNativeStateImageList()
{
if (stateImageList != null && stateImageList.Images.Count > 0)
{
ImageList newImageList = new ImageList();
if (ScaledStateImageSize != null)
{
newImageList.ImageSize = (Size)ScaledStateImageSize;
}
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];
}
newImageList.Images.AddRange(images);
SendMessage(NativeMethods.TVM_SETIMAGELIST, NativeMethods.TVSIL_STATE, newImageList.Handle);
if (internalStateImageList != null)
{
internalStateImageList.Dispose();
}
internalStateImageList = newImageList;
}
}
private void SetStateImageList(IntPtr handle)
{
// In certain cases (TREEVIEWSTATE_checkBoxes) e.g., the Native TreeView leaks the imagelist
// even if set by us. To prevent any leaks, we always destroy what was there after setting a new list.
IntPtr handleOld = SendMessage(NativeMethods.TVM_SETIMAGELIST, NativeMethods.TVSIL_STATE, handle);
if ((handleOld != IntPtr.Zero) && (handleOld != handle))
{
SafeNativeMethods.ImageList_Destroy_Native(new HandleRef(this, handleOld));
}
}
// Destroying the tree-view control does not destroy the native state image list. See DevDiv #303906
// We must destroy it explicitly.
private void DestroyNativeStateImageList(bool reset)
{
IntPtr handle = SendMessage(NativeMethods.TVM_GETIMAGELIST, NativeMethods.TVSIL_STATE, IntPtr.Zero);
if (handle != IntPtr.Zero)
{
SafeNativeMethods.ImageList_Destroy_Native(new HandleRef(this, handle));
if (reset)
{
SendMessage(NativeMethods.TVM_SETIMAGELIST, NativeMethods.TVSIL_STATE, IntPtr.Zero);
}
}
}
/// <include file='doc\TreeView.uex' path='docs/doc[@for="TreeView.OnHandleDestroyed"]/*' />
/// <devdoc>
/// </devdoc>
/// <internalonly/>
protected override void OnHandleDestroyed(EventArgs e) {
selectedNode = SelectedNode;
// Unfortunately, to avoid the native tree view leaking it's State Image List, we need to
// destroy it ourselves here. See DevDiv Bug #307248.
DestroyNativeStateImageList(true);
// for the case when we are NOT being disposed, we'll be recreating the internal state imagelist
// in OnHandleCreate, so it is ok to completely Dispose here
if (internalStateImageList != null)
{
internalStateImageList.Dispose();
internalStateImageList = null;
}
base.OnHandleDestroyed(e);
}
/// <include file='doc\TreeView.uex' path='docs/doc[@for="TreeView.OnMouseHover"]/*' />
/// <devdoc>
/// We keep track of if we've hovered already so we don't fire multiple hover events
/// </devdoc>
/// <internalonly>
protected override void OnMouseLeave(EventArgs e) {
hoveredAlready = false;
base.OnMouseLeave(e);
}
/// <include file='doc\TreeView.uex' path='docs/doc[@for="TreeView.OnMouseHover"]/*' />
/// <devdoc>
/// 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.
/// </devdoc>
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();
}
/// <include file='doc\TreeView.uex' path='docs/doc[@for="TreeView.OnBeforeLabelEdit"]/*' />
/// <devdoc>
/// Fires the beforeLabelEdit event.
/// </devdoc>
protected virtual void OnBeforeLabelEdit(NodeLabelEditEventArgs e) {
Contract.Requires(e != null);
if (onBeforeLabelEdit != null) onBeforeLabelEdit(this, e);
}
/// <include file='doc\TreeView.uex' path='docs/doc[@for="TreeView.OnAfterLabelEdit"]/*' />
/// <devdoc>
/// Fires the afterLabelEdit event.
/// </devdoc>
protected virtual void OnAfterLabelEdit(NodeLabelEditEventArgs e) {
Contract.Requires(e != null);
if (onAfterLabelEdit != null) onAfterLabelEdit(this, e);
}
/// <include file='doc\TreeView.uex' path='docs/doc[@for="TreeView.OnBeforeCheck"]/*' />
/// <devdoc>
/// Fires the beforeCheck event.
/// </devdoc>
protected virtual void OnBeforeCheck(TreeViewCancelEventArgs e) {
Contract.Requires(e != null);
if (onBeforeCheck != null) onBeforeCheck(this, e);
}
/// <include file='doc\TreeView.uex' path='docs/doc[@for="TreeView.OnAfterCheck"]/*' />
/// <devdoc>
/// Fires the afterCheck event.
/// </devdoc>
protected virtual void OnAfterCheck(TreeViewEventArgs e) {
Contract.Requires(e != null);
if (onAfterCheck != null) onAfterCheck(this, e);
}
/// <include file='doc\TreeView.uex' path='docs/doc[@for="TreeView.OnBeforeCollapse"]/*' />
/// <devdoc>
/// Fires the beforeCollapse event.
/// </devdoc>
protected internal virtual void OnBeforeCollapse(TreeViewCancelEventArgs e) {
Contract.Requires(e != null);
if (onBeforeCollapse != null) onBeforeCollapse(this, e);
}
/// <include file='doc\TreeView.uex' path='docs/doc[@for="TreeView.OnAfterCollapse"]/*' />
/// <devdoc>
/// Fires the afterCollapse event.
/// </devdoc>
protected internal virtual void OnAfterCollapse(TreeViewEventArgs e) {
Contract.Requires(e != null);
if (onAfterCollapse != null) onAfterCollapse(this, e);
}
/// <include file='doc\TreeView.uex' path='docs/doc[@for="TreeView.OnBeforeExpand"]/*' />
/// <devdoc>
/// Fires the beforeExpand event.
/// </devdoc>
protected virtual void OnBeforeExpand(TreeViewCancelEventArgs e) {
Contract.Requires(e != null);
if (onBeforeExpand != null) onBeforeExpand(this, e);
}
/// <include file='doc\TreeView.uex' path='docs/doc[@for="TreeView.OnAfterExpand"]/*' />
/// <devdoc>
/// Fires the afterExpand event.
/// </devdoc>
protected virtual void OnAfterExpand(TreeViewEventArgs e) {
Contract.Requires(e != null);
if (onAfterExpand != null) onAfterExpand(this, e);
}
/// <include file='doc\TreeView.uex' path='docs/doc[@for="TreeView.OnItemDrag"]/*' />
/// <devdoc>
/// Fires the ItemDrag event.
/// </devdoc>
protected virtual void OnItemDrag(ItemDragEventArgs e) {
Contract.Requires(e != null);
if (onItemDrag != null) onItemDrag(this, e);
}
/// <include file='doc\TreeView.uex' path='docs/doc[@for="TreeView.OnNodeMouseHover"]/*' />
/// <devdoc>
/// Fires the NodeMouseHover event.
/// </devdoc>
protected virtual void OnNodeMouseHover(TreeNodeMouseHoverEventArgs e) {
Contract.Requires(e != null);
if (onNodeMouseHover != null) onNodeMouseHover(this, e);
}
/// <include file='doc\TreeView.uex' path='docs/doc[@for="TreeView.OnBeforeSelect"]/*' />
/// <devdoc>
/// Fires the beforeSelect event.
/// </devdoc>
protected virtual void OnBeforeSelect(TreeViewCancelEventArgs e) {
Contract.Requires(e != null);
if (onBeforeSelect != null) onBeforeSelect(this, e);
}
/// <include file='doc\TreeView.uex' path='docs/doc[@for="TreeView.OnAfterSelect"]/*' />
/// <devdoc>
/// Fires the afterSelect event.
/// </devdoc>
protected virtual void OnAfterSelect(TreeViewEventArgs e) {
Contract.Requires(e != null);
if (onAfterSelect != null) onAfterSelect(this, e);
}
/// <include file='doc\TreeView.uex' path='docs/doc[@for="TreeView.onNodeMouseClick"]/*' />
/// <devdoc>
/// Fires the onNodeMouseClick event.
/// </devdoc>
protected virtual void OnNodeMouseClick(TreeNodeMouseClickEventArgs e) {
Contract.Requires(e != null);
if (onNodeMouseClick != null) onNodeMouseClick(this, e);
}
/// <include file='doc\TreeView.uex' path='docs/doc[@for="TreeView.onNodeMouseDoubleClick"]/*' />
/// <devdoc>
/// Fires the onNodeMouseDoubleClick event.
/// </devdoc>
protected virtual void OnNodeMouseDoubleClick(TreeNodeMouseClickEventArgs e) {
Contract.Requires(e != null);
if (onNodeMouseDoubleClick != null) onNodeMouseDoubleClick(this, e);
}
/// <include file='doc\TreeView.uex' path='docs/doc[@for="TreeView.OnKeyDown"]/*' />
/// <devdoc>
/// Handles the OnBeforeCheck / OnAfterCheck for keyboard clicks
/// </devdoc>
/// <internalonly/>
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;
}
}
}
/// <include file='doc\TreeView.uex' path='docs/doc[@for="TreeView.OnKeyUp"]/*' />
/// <devdoc>
/// Handles the OnBeforeCheck / OnAfterCheck for keyboard clicks
/// </devdoc>
/// <internalonly/>
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;
}
}
/// <include file='doc\TreeView.uex' path='docs/doc[@for="TreeView.OnKeyPress"]/*' />
/// <devdoc>
/// Handles the OnBeforeCheck / OnAfterCheck for keyboard clicks
/// </devdoc>
/// <internalonly/>
protected override void OnKeyPress(KeyPressEventArgs e) {
base.OnKeyPress(e);
if (e.Handled) return;
// eat the space key
if (e.KeyChar == ' ') e.Handled = true;
}
/// <include file='doc\Form.uex' path='docs/doc[@for="Form.OnRightToLeftLayoutChanged"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
[EditorBrowsable(EditorBrowsableState.Advanced)]
protected virtual void OnRightToLeftLayoutChanged(EventArgs e) {
Contract.Requires(e != null);
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);
}
/// <include file='doc\TreeView.uex' path='docs/doc[@for="TreeView.ResetIndent"]/*' />
/// <devdoc>
/// This resets the indentation to the system default.
/// </devdoc>
private void ResetIndent() {
indent = -1;
// is this overkill?
RecreateHandle();
}
/// <include file='doc\TreeView.uex' path='docs/doc[@for="TreeView.ResetItemHeight"]/*' />
/// <devdoc>
/// This resets the item height to the system default.
/// </devdoc>
private void ResetItemHeight() {
itemHeight = -1;
RecreateHandle();
}
/// <include file='doc\TreeView.uex' path='docs/doc[@for="TreeView.ShouldSerializeIndent"]/*' />
/// <devdoc>
/// Retrieves true if the indent should be persisted in code gen.
/// </devdoc>
private bool ShouldSerializeIndent() {
return(indent != -1);
}
/// <include file='doc\TreeView.uex' path='docs/doc[@for="TreeView.ShouldSerializeItemHeight"]/*' />
/// <devdoc>
/// Retrieves true if the itemHeight should be persisted in code gen.
/// </devdoc>
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);
}
/// <include file='doc\TreeView.uex' path='docs/doc[@for="TreeView.Sort"]/*' />
/// <devdoc>
/// Updated the sorted order
/// </devdoc>
public void Sort() {
Sorted = true;
RefreshNodes();
}
/// <include file='doc\TreeView.uex' path='docs/doc[@for="TreeView.ToString"]/*' />
/// <devdoc>
/// Returns a string representation for this control.
/// </devdoc>
/// <internalonly/>
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) {
SetStateImageList(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 sync.
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);
}
}
/// <include file='doc\TreeView.uex' path='docs/doc[@for="TreeView.CustomDraw"]/*' />
/// <devdoc>
/// Performs custom draw handling
/// </devdoc>
/// <internalonly/>
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;
}
}
/// <include file='doc\TreeView.uex' path='docs/doc[@for="TreeView.GetItemRenderStyles"]/*' />
/// <devdoc>
/// 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.
///
/// </devdoc>
/// <internalonly/>
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;
}
/// <include file='doc\TreeView.uex' path='docs/doc[@for="TreeView.WmNeedText"]/*' />
/// <devdoc>
/// </devdoc>
/// <internalonly/>
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;
}
}
}
/// <include file='doc\Treenode.uex' path='docs/doc[@for="Treenode.ShowContextMenu"]/*' />
/// <devdoc>
/// Shows the context menu for the Treenode.
/// </devdoc>
/// <internalonly/>
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();
}
}
}
/// <include file='doc\TreeView.uex' path='docs/doc[@for="TreeView.WndProc"]/*' />
/// <devdoc>
/// </devdoc>
/// <internalonly/>
[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);
this.InvokeGotFocus(this, 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;
}
}
}
}
|