File: winforms\Managed\System\WinForms\PropertyGridInternal\PropertyGridView.cs
Project: ndp\fx\src\System.Windows.Forms.csproj (System.Windows.Forms)
//------------------------------------------------------------------------------
// <copyright file="PropertyGridView.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>                                                                
//------------------------------------------------------------------------------
 
/*
 */
namespace System.Windows.Forms.PropertyGridInternal {
    using System.Security.Permissions;
    using System.Runtime.Serialization.Formatters;
    using System.Threading;
    using System.Runtime.InteropServices;
    using System.Runtime.Remoting;
    using System.Runtime.Versioning;
    using System.ComponentModel;
    using System.Diagnostics;
    using System.Diagnostics.CodeAnalysis;
    using System.Globalization;
    using System;
    using System.Collections;
    using System.Windows.Forms;
    using System.Windows.Forms.Internal;
    using System.Windows.Forms.ComponentModel;
    using System.Windows.Forms.Design;
    using System.ComponentModel.Design;
    using System.Windows.Forms.VisualStyles;
    using System.IO;
    using System.Drawing;
    using System.Drawing.Design;
    using Microsoft.Win32;
    using Message = System.Windows.Forms.Message;
    using System.Drawing.Drawing2D;
 
    internal class PropertyGridView :
    Control,
    IWin32Window,
    IWindowsFormsEditorService,
    IServiceProvider {
 
        protected static readonly Point InvalidPoint = new Point(int.MinValue, int.MinValue);
 
#if true // RENDERMODE
        public const int RENDERMODE_LEFTDOT = 2;
        public const int RENDERMODE_BOLD = 3;
        public const int RENDERMODE_TRIANGLE = 4;
 
        public static int inheritRenderMode = RENDERMODE_BOLD;
#endif
 
 
        public static TraceSwitch GridViewDebugPaint = new TraceSwitch("GridViewDebugPaint", "PropertyGridView: Debug property painting");
 
 
        private PropertyGrid ownerGrid;                      // the properties window host.
 
 
#if true // RENDERMODE
        private const int LEFTDOT_SIZE = 4;
#endif
        // constants
        private const int       EDIT_INDENT = 0;
        private const int       OUTLINE_INDENT = 10;
        private const int       OUTLINE_SIZE = 9;
        private const int       OUTLINE_SIZE_EXPLORER_TREE_STYLE = 16;
        private  int            outlineSize = OUTLINE_SIZE;
        private  int            outlineSizeExplorerTreeStyle = OUTLINE_SIZE_EXPLORER_TREE_STYLE;
        private const int       PAINT_WIDTH = 20;
        private int             paintWidth = PAINT_WIDTH;
        private const int       PAINT_INDENT = 26;
        private int             paintIndent = PAINT_INDENT;
        private const int       ROWLABEL = 1;
        private const int       ROWVALUE = 2;
        private const int       MAX_LISTBOX_HEIGHT = 200;
        private int             maxListBoxHeight = MAX_LISTBOX_HEIGHT;
        
        private const short    ERROR_NONE = 0;
        private const short    ERROR_THROWN = 1;
        private const short    ERROR_MSGBOX_UP = 2;
        internal  const short    GDIPLUS_SPACE = 2;
        internal  const int      MaxRecurseExpand = 10;
 
        private const int DOTDOTDOT_ICONWIDTH = 7;
        private const int DOTDOTDOT_ICONHEIGHT = 8;
        private const int DOWNARROW_ICONWIDTH = 16;
        private const int DOWNARROW_ICONHEIGHT = 16;
 
        private static int OFFSET_2PIXELS = 2;
        private int offset_2Units = OFFSET_2PIXELS;
 
        protected static readonly Point InvalidPosition = new Point(int.MinValue, int.MinValue);
 
 
        // colors and fonts
        private Brush                               backgroundBrush = null;
        private   Font                              fontBold = null;
        private Color                               grayTextColor;
 
        // for backwards compatibility of default colors
        private bool                                grayTextColorModified = false; // true if someone has set the grayTextColor property
 
        // property collections
        private GridEntryCollection                 topLevelGridEntries = null;     // top level props
        private GridEntryCollection                 allGridEntries = null;  // cache of viewable props
        
        // row information
        internal  int                               totalProps = -1;        // # of viewable props
        private   int                               visibleRows = -1;         // # of visible rows
        private   int                               labelWidth = -1;
        public double                               labelRatio = 2; // ratio of whole row to label width
        
        private short                               requiredLabelPaintMargin = GDIPLUS_SPACE;
 
        // current selected row and tooltip.
        private   int                               selectedRow = -1;
        private GridEntry                           selectedGridEntry = null;
        private   int                               tipInfo = -1;
 
        // editors & controls
        private   GridViewEdit                      edit = null;
        private   DropDownButton                    btnDropDown = null;
        private   DropDownButton                    btnDialog = null;
        private   GridViewListBox                   listBox = null;
        private   DropDownHolder                    dropDownHolder = null;
        private   Rectangle                         lastClientRect = Rectangle.Empty;
        private   Control                           currentEditor = null;
        private   ScrollBar                         scrollBar = null;
        internal  GridToolTip                       toolTip = null;
        private   GridErrorDlg                      errorDlg = null;
        
 
        // flags
        private const short FlagNeedsRefresh            = 0x0001;
        private const short FlagIsNewSelection          = 0x0002;
        private const short FlagIsSplitterMove          = 0x0004;
        private const short FlagIsSpecialKey            = 0x0008;
        private const short FlagInPropertySet           = 0x0010;
        private const short FlagDropDownClosing         = 0x0020;
        private const short FlagDropDownCommit          = 0x0040;
        private const short FlagNeedUpdateUIBasedOnFont = 0x0080;
        private const short FlagBtnLaunchedEditor       = 0x0100;
        private const short FlagNoDefault               = 0x0200;
        private const short FlagResizableDropDown       = 0x0400;
        
 
        private   short                             flags = FlagNeedsRefresh | FlagIsNewSelection | FlagNeedUpdateUIBasedOnFont;
        private   short                             errorState = ERROR_NONE;
 
        private   Point                             ptOurLocation = new Point(1,1);
        
        private   string                            originalTextValue = null;     // original text, in case of ESC
        private   int                               cumulativeVerticalWheelDelta = 0;
        private   long                              rowSelectTime = 0;
        private   Point                             rowSelectPos = Point.Empty; // the position that we clicked on a row to test for double clicks
        private   Point                             lastMouseDown = InvalidPosition;
        private   int                               lastMouseMove;
        private   GridEntry                         lastClickedEntry;
        
        private IServiceProvider                    serviceProvider;
        private IHelpService                        topHelpService;
        private IHelpService                        helpService;
 
        private EventHandler                        ehValueClick;
        private EventHandler                        ehLabelClick;
        private EventHandler                        ehOutlineClick;
        private EventHandler                        ehValueDblClick;
        private EventHandler                        ehLabelDblClick;
        private GridEntryRecreateChildrenEventHandler ehRecreateChildren;
 
        private int                                 cachedRowHeight = -1;
        IntPtr baseHfont;
        IntPtr boldHfont;
 
        [
            SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters") // PropertyGridView's caption is not visible.
                                                                                                        // So we don't have to localize its text.
        ]
        public PropertyGridView(IServiceProvider serviceProvider, PropertyGrid propertyGrid)
        : base() {
            if (DpiHelper.IsScalingRequired) {
                paintWidth = DpiHelper.LogicalToDeviceUnitsX(PAINT_WIDTH);
                paintIndent = DpiHelper.LogicalToDeviceUnitsX(PAINT_INDENT);
                outlineSizeExplorerTreeStyle = DpiHelper.LogicalToDeviceUnitsX(OUTLINE_SIZE_EXPLORER_TREE_STYLE);
                outlineSize = DpiHelper.LogicalToDeviceUnitsX(OUTLINE_SIZE);
                maxListBoxHeight = DpiHelper.LogicalToDeviceUnitsY(MAX_LISTBOX_HEIGHT);
            }
                    
            this.ehValueClick = new EventHandler(this.OnGridEntryValueClick);
            this.ehLabelClick = new EventHandler(this.OnGridEntryLabelClick);
            this.ehOutlineClick = new EventHandler(this.OnGridEntryOutlineClick);
            this.ehValueDblClick = new EventHandler(this.OnGridEntryValueDoubleClick);
            this.ehLabelDblClick = new EventHandler(this.OnGridEntryLabelDoubleClick);
            this.ehRecreateChildren = new GridEntryRecreateChildrenEventHandler(this.OnRecreateChildren);
 
            ownerGrid = propertyGrid;
            this.serviceProvider = serviceProvider;
            
            SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
            SetStyle(ControlStyles.ResizeRedraw, false);
            SetStyle(ControlStyles.UserMouse, true);
            
 
            // properties
            BackColor = SystemColors.Window;
            ForeColor = SystemColors.WindowText;
            grayTextColor = SystemColors.GrayText;
            backgroundBrush = SystemBrushes.Window;
            TabStop = true;
            
            this.Text = "PropertyGridView";
 
            CreateUI();
            LayoutWindow(true); 
        }
        
        public override Color BackColor {
            get {
                return base.BackColor;
            }
            set {
                this.backgroundBrush = new SolidBrush(value);
                base.BackColor = value;
            }
        }
 
        internal Brush GetBackgroundBrush(Graphics g) {
            return backgroundBrush;
        }
        
        [Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced)]
        public bool CanCopy {
            get {                
                if (selectedGridEntry == null) {
                    return false;
                }
 
                if (!Edit.Focused) {
                    string val = selectedGridEntry.GetPropertyTextValue();
 
                    return val != null && val.Length > 0;
                }
                
                return true;
            }
        }
        
        [Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced)]
        public bool CanCut {
            get {
                return CanCopy && selectedGridEntry.IsTextEditable; 
            }
        }
        
        [Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced)]
        public bool CanPaste {
            get {
                return selectedGridEntry != null && selectedGridEntry.IsTextEditable; // return gridView.CanPaste;
            }
        }
        
        [Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced)]
        public bool CanUndo {
            get {
                if (!Edit.Visible || !Edit.Focused) {
                    return false;
                }
                return (0 != (int)Edit.SendMessage(NativeMethods.EM_CANUNDO, 0, 0));
            }
        }
 
        internal DropDownButton DropDownButton {
            get {
                if (btnDropDown == null) {
                    #if DEBUG
                        if (ownerGrid.inGridViewCreate) {
                            throw new Exception("PERF REGRESSION - Creating item in grid view create");
                        }
                    #endif
                    
                    btnDropDown = new DropDownButton();
                    btnDropDown.UseComboBoxTheme = true;
                    Bitmap bitmap = CreateResizedBitmap("Arrow.ico", DOWNARROW_ICONWIDTH, DOWNARROW_ICONHEIGHT);
                    btnDropDown.Image = bitmap;
                    btnDropDown.BackColor = SystemColors.Control;
                    btnDropDown.ForeColor = SystemColors.ControlText;
                    btnDropDown.Click += new EventHandler(this.OnBtnClick);
                    btnDropDown.GotFocus += new EventHandler(OnDropDownButtonGotFocus);
                    btnDropDown.LostFocus += new EventHandler(this.OnChildLostFocus);
                    btnDropDown.TabIndex = 2;
                    CommonEditorSetup(btnDropDown);
                    btnDropDown.Size = DpiHelper.EnableDpiChangedHighDpiImprovements ? new Size(SystemInformation.VerticalScrollBarArrowHeightForDpi(this.deviceDpi), RowHeight) : new Size(SystemInformation.VerticalScrollBarArrowHeight, RowHeight);
                }
                return btnDropDown;
            }
        }
 
        private Button DialogButton {
            get {
                if (btnDialog == null) {
 
                    #if DEBUG
                    if (ownerGrid.inGridViewCreate) {
                        throw new Exception("PERF REGRESSION - Creating item in grid view create");
                    }
                    #endif
                    btnDialog = new DropDownButton();
                    btnDialog.BackColor = SystemColors.Control;
                    btnDialog.ForeColor = SystemColors.ControlText;
                    btnDialog.TabIndex = 3;
                    btnDialog.Image = CreateResizedBitmap("dotdotdot.ico", DOTDOTDOT_ICONWIDTH, DOTDOTDOT_ICONHEIGHT);
                    btnDialog.Click += new EventHandler(this.OnBtnClick);
                    btnDialog.KeyDown += new KeyEventHandler(this.OnBtnKeyDown);
                    btnDialog.GotFocus += new EventHandler(OnDropDownButtonGotFocus);
                    btnDialog.LostFocus += new EventHandler(this.OnChildLostFocus);
                    btnDialog.Size = DpiHelper.EnableDpiChangedHighDpiImprovements ? new Size(SystemInformation.VerticalScrollBarArrowHeightForDpi(this.deviceDpi), RowHeight) : new Size(SystemInformation.VerticalScrollBarArrowHeight, RowHeight);
                    CommonEditorSetup(btnDialog);
                }
                return btnDialog;
            }
        }
 
        [ResourceExposure(ResourceScope.Machine)]
        [ResourceConsumption(ResourceScope.Machine)]
        private static Bitmap GetBitmapFromIcon(string iconName, int iconsWidth, int iconsHeight) {
            Size desiredSize = new Size(iconsWidth, iconsHeight);
            Icon icon = new Icon(BitmapSelector.GetResourceStream(typeof(PropertyGrid), iconName), desiredSize);
            Bitmap b = icon.ToBitmap();
            icon.Dispose();
            
            if ((DpiHelper.IsScalingRequired || DpiHelper.EnableDpiChangedHighDpiImprovements) && (b.Size.Width != iconsWidth || b.Size.Height != iconsHeight)) {
                Bitmap scaledBitmap = DpiHelper.CreateResizedBitmap(b, desiredSize);
                if (scaledBitmap != null) {
                    b.Dispose();
                    b = scaledBitmap;
                }
            }
            return b;
        }
 
        private GridViewEdit Edit {
            get{
                if (edit == null) {
 
                    #if DEBUG
                    if (ownerGrid.inGridViewCreate) {
                        throw new Exception("PERF REGRESSION - Creating item in grid view create");
                    }
                    #endif
 
                    edit = new GridViewEdit(this);
                    edit.BorderStyle = BorderStyle.None;
                    edit.AutoSize = false;
                    edit.TabStop = false;
                    edit.AcceptsReturn = true;
                    edit.BackColor = BackColor;
                    edit.ForeColor = ForeColor;
                    edit.KeyDown += new KeyEventHandler(this.OnEditKeyDown);
                    edit.KeyPress += new KeyPressEventHandler(this.OnEditKeyPress);
                    edit.GotFocus += new EventHandler(this.OnEditGotFocus);
                    edit.LostFocus += new EventHandler(this.OnEditLostFocus);
                    edit.MouseDown += new MouseEventHandler(this.OnEditMouseDown);
                    edit.TextChanged += new EventHandler(this.OnEditChange);
                    //edit.ImeModeChanged += new EventHandler(this.OnEditImeModeChanged);
                    edit.TabIndex = 1;
                    CommonEditorSetup(edit);
                }
                return edit;
            }
        }
 
        /// <summary>
        /// Represents the Editor's control accessible object.
        /// </summary>
        internal AccessibleObject EditAccessibleObject {
            get {
                return Edit.AccessibilityObject;
            }
        }
 
        private GridViewListBox DropDownListBox {
            get {
                if (listBox == null) {
                    #if DEBUG
                    if (ownerGrid.inGridViewCreate) {
                        throw new Exception("PERF REGRESSION - Creating item in grid view create");
                    }
                    #endif
 
                    listBox = new GridViewListBox(this);
                    listBox.DrawMode = DrawMode.OwnerDrawFixed;
                    //listBox.Click += new EventHandler(this.OnListClick);
                    listBox.MouseUp += new MouseEventHandler(this.OnListMouseUp);
                    listBox.DrawItem += new DrawItemEventHandler(this.OnListDrawItem);
                    listBox.SelectedIndexChanged += new EventHandler(this.OnListChange);
                    listBox.KeyDown += new KeyEventHandler(this.OnListKeyDown);
                    listBox.LostFocus += new EventHandler(this.OnChildLostFocus);
                    listBox.Visible = true;
                    listBox.ItemHeight = RowHeight;
                }
                return listBox;
            }
        }
 
        /// <summary>
        /// Represents the DropDownListBox accessible object.
        /// </summary>
        internal AccessibleObject DropDownListBoxAccessibleObject {
            get {
                if (DropDownListBox.Visible) {
                    return DropDownListBox.AccessibilityObject;
                }
 
                return null;
            }
        }
        
        internal bool DrawValuesRightToLeft {
            get {
                if (edit != null && edit.IsHandleCreated) {
                    int exStyle = unchecked((int)((long)UnsafeNativeMethods.GetWindowLong(new HandleRef(edit, edit.Handle), NativeMethods.GWL_EXSTYLE)));
                    return ((exStyle & NativeMethods.WS_EX_RTLREADING) != 0);
                }
                else {
                    return false;
                }
            }
        }
 
        internal bool DropDownVisible {
            get {
                return dropDownHolder != null && dropDownHolder.Visible;
            }
        }
        
        public bool FocusInside {
            get {
                return(this.ContainsFocus || (dropDownHolder != null && dropDownHolder.ContainsFocus));
            }
        }
        
        internal Color GrayTextColor{
            get {
                // if changed from the default, then the set value is returned
                if (grayTextColorModified) {
                    return grayTextColor;
                }
 
                if (this.ForeColor.ToArgb() == SystemColors.WindowText.ToArgb()) {
                    return SystemColors.GrayText;
                }
                
                // compute the new color by halving the value of the old one.
                //
                int colorRGB = this.ForeColor.ToArgb();
                
                int alphaValue = (colorRGB >> 24) & 0xff;
                if (alphaValue != 0) {
                    alphaValue /= 2;
                    colorRGB &= 0xFFFFFF;
                    colorRGB |= (int)((alphaValue << 24) & 0xFF000000);
                }
                else {
                    colorRGB /= 2;
                }
                return Color.FromArgb(colorRGB);
            }
            set {
                grayTextColor = value;
                grayTextColorModified = true;
            }
        }
 
       //   This dialog's width is defined by the summary message 
       //   in the top pane. We don't restrict dialog width in any way. 
       //   Use caution and check at all DPI scaling factors if adding a new message
       //   to be displayed in the top pane.
        private GridErrorDlg ErrorDialog {
            get {
                if (this.errorDlg == null) {
                    using (DpiHelper.EnterDpiAwarenessScope(DpiAwarenessContext.DPI_AWARENESS_CONTEXT_SYSTEM_AWARE)) {
                        errorDlg = new GridErrorDlg(this.ownerGrid);
                    }
                }
                return errorDlg;
            }
        }
 
        private bool HasEntries {
            get{
                return topLevelGridEntries != null && topLevelGridEntries.Count > 0;
            }
        }
        
        protected int InternalLabelWidth {
            get {
                if (GetFlag(FlagNeedUpdateUIBasedOnFont)) {
                    UpdateUIBasedOnFont(true);
                }
                if (labelWidth == -1) {
                    SetConstants();
                }
                return labelWidth;
            }
        }
        
        internal int LabelPaintMargin {
  
            set {
                requiredLabelPaintMargin = (short)Math.Max(Math.Max(value, requiredLabelPaintMargin), GDIPLUS_SPACE);
            }
        }
 
        protected bool NeedsCommit{
            get {
                string text;
 
                if (edit==null || !Edit.Visible) {
                    return false;
                }
 
                text = Edit.Text;
 
                if (((text == null || text.Length == 0) && (originalTextValue == null || originalTextValue.Length == 0)) ||
                    (text != null && originalTextValue != null && text.Equals(originalTextValue))) {
                    return false;
                }
                return true;
            }
        }
 
        public PropertyGrid OwnerGrid{
            get{
                return this.ownerGrid;
            }
        }
 
        protected int RowHeight {
            get {
                if (cachedRowHeight == -1) {
                    cachedRowHeight = (int)Font.Height + 2;
                }
                return cachedRowHeight;
            }
        }
 
        /// <include file='doc\PropertyGridView.uex' path='docs/doc[@for="PropertyGridView.ContextMenuDefaultLocation"]/*' />
        /// <devdoc>
        /// Returns a default location for showing the context menu.  This
        /// location is the center of the active property label in the grid, and
        /// is used useful to position the context menu when the menu is invoked
        /// via the keyboard.
        /// </devdoc>
        public Point ContextMenuDefaultLocation {
            get {
                // get the rect for the currently selected prop name, find the middle
                Rectangle rect = GetRectangle( selectedRow, ROWLABEL );
                Point pt = PointToScreen( new Point( rect.X, rect.Y ) );
                return new Point(pt.X + (rect.Width / 2), pt.Y + (rect.Height / 2));
            }
        }
 
        private ScrollBar ScrollBar {
            get {
                if (scrollBar == null) {
                    #if DEBUG
                    if (ownerGrid.inGridViewCreate) {
                        throw new Exception("PERF REGRESSION - Creating item in grid view create");
                    }
                    #endif
                    scrollBar = new VScrollBar();
                    scrollBar.Scroll += new ScrollEventHandler(this.OnScroll);
                    Controls.Add(scrollBar);
                }
                return scrollBar;
            }
        }
        
        internal GridEntry SelectedGridEntry {
            get {
                return selectedGridEntry;
            }
            set {
                if (allGridEntries != null) {
                    foreach (GridEntry e in allGridEntries) {
                        if (e == value) {
                            SelectGridEntry(value, true);
                            return;
                        }
                    }
                }
                
                GridEntry gr = FindEquivalentGridEntry(new GridEntryCollection(null, new GridEntry[]{value}));
                
                if (gr != null) {
                    SelectGridEntry(gr, true);
                    return;
                }
                
                throw new ArgumentException(SR.GetString(SR.PropertyGridInvalidGridEntry));
            }
        }
 
        /*
        public PropertyDescriptor SelectedPropertyDescriptor {
            get {
                if (selectedGridEntry != null && (selectedGridEntry is PropertyDescriptorGridEntry)) {
                    return ((PropertyDescriptorGridEntry) selectedGridEntry).PropertyDescriptor;
                }
                else {
                    return null;
                }
            }
        }
        */
 
        /*
        /// <include file='doc\PropertyGridView.uex' path='docs/doc[@for="PropertyGridView.SelectedPropertyName"]/*' />
        /// <devdoc>
        /// Returns the currently selected property name.
        /// If no property or a category name is selected, "" is returned.
        /// If the category is a sub property, it is concatenated onto its
        /// parent property name with a ".".
        /// </devdoc>
        public string SelectedPropertyName {
            get {
                if (selectedGridEntry == null) {
                    return "";
                }
                GridEntry gridEntry = selectedGridEntry;
                string name = "";
                while (gridEntry != null && gridEntry.PropertyDepth >= 0) {
                    if (name.Length > 0) {
                        name = gridEntry.PropertyName + "." + name;
                    }
                    else {
                        name = gridEntry.PropertyName;
                    }
                    gridEntry = gridEntry.ParentGridEntry;
                }
                return name;
            }
            set{
                if (value==null){
                    return;
                }
                if (value.Equals(selectedGridEntry.PropertyLabel)){
                    return;
                }
 
                string curName;
                string remain = value;
 
                int dot = remain.IndexOf('.');
                GridEntry[] ipes = GetAllGridEntries();
                int pos = 0;
 
                while (dot != -1){
                    curName = remain.Substring(0, dot);
                    Debug.WriteLine("Looking for: " + curName);
                    for (int i = pos; i < ipes.Length ; i++){
                        Debug.WriteLine("Checking : " + ipes[i].PropertyLabel);
                        if (ipes[i].PropertyLabel.Equals(curName)){
                            if (ipes[i].Expandable){
                                pos = i;
                                remain = remain.Substring(dot + 1);
                                dot = remain.IndexOf('.');
                                if (dot != -1){
                                    Debug.WriteLine("Expanding: " + ipes[i].PropertyLabel);
                                    ipes[i].SetPropertyExpand(true);
                                    ipes = GetAllGridEntries();
                                    break;
                                }
                                else{
                                    SelectGridEntry(ipes[i], true);
                                    return;
                                }
                            }
                        }
                    }
                    // uh oh
                    dot = -1;
                }
                // oops, didn't find it
                SelectRow(0);
                return;
 
            }
        }
        */
        
        /// <include file='doc\PropertyGridView.uex' path='docs/doc[@for="PropertyGridView.ServiceProvider"]/*' />
        /// <devdoc>
        /// Returns or sets the IServiceProvider the PropertyGridView will use to obtain
        /// services.  This may be null.
        /// </devdoc>
        public IServiceProvider ServiceProvider {
            get { 
               return serviceProvider;
            }
            set {
               if (value != serviceProvider) {
                    this.serviceProvider = value;
                    
                    topHelpService = null;
 
                    if (helpService != null && helpService is IDisposable)
                        ((IDisposable)helpService).Dispose();
 
                    helpService = null;
               }
            }
        }
 
        /// <summary>
        /// Indicates whether or not the control supports UIA Providers via
        /// IRawElementProviderFragment/IRawElementProviderFragmentRoot interfaces.
        /// </summary>
        internal override bool SupportsUiaProviders {
            get {
                return AccessibilityImprovements.Level3;
            }
        }
 
        private int TipColumn {
            get{
                return(tipInfo & unchecked((int)0xFFFF0000)) >> 16;
            }
            set{
 
                // clear the column
                tipInfo &= 0xFFFF;
 
                // set the row
                tipInfo |= ((value & 0xFFFF) << 16);
            }
        }
 
        private int TipRow {
            get{
                return tipInfo & 0xFFFF;
            }
            set{
 
                // clear the row
                tipInfo &= unchecked((int)0xFFFF0000);
 
                // set the row
                tipInfo |= (value & 0xFFFF);
            }
        }
 
        private GridToolTip ToolTip {
            get {
                if (toolTip == null) {
                    #if DEBUG
                    if (ownerGrid.inGridViewCreate) {
                        throw new Exception("PERF REGRESSION - Creating item in grid view create");
                    }
                    #endif
                    toolTip = new GridToolTip(new Control[]{this, Edit});
                    toolTip.ToolTip = "";
                    toolTip.Font = this.Font;
                }   
                return toolTip;
            }
        }
 
        /// <summary>
        /// Gets the top level grid entries.
        /// </summary>
        internal GridEntryCollection TopLevelGridEntries {
            get {
                return topLevelGridEntries;
            }
        }
 
        internal GridEntryCollection AccessibilityGetGridEntries() {
            return GetAllGridEntries();
        }
 
        internal Rectangle AccessibilityGetGridEntryBounds(GridEntry gridEntry) {
            int row = GetRowFromGridEntry(gridEntry);
            if (row == -1) {
                return new Rectangle(0, 0, 0, 0);
            }
            Rectangle rect = GetRectangle(row, ROWVALUE | ROWLABEL);
 
            // Translate rect to screen coordinates
            //
            NativeMethods.POINT pt = new NativeMethods.POINT(rect.X, rect.Y);
            UnsafeNativeMethods.ClientToScreen(new HandleRef(this, Handle), pt);
 
            return new Rectangle(pt.x, pt.y, rect.Width, rect.Height);
        }
        
        internal int AccessibilityGetGridEntryChildID(GridEntry gridEntry) {
         
            GridEntryCollection ipes = GetAllGridEntries();
            
            if (ipes == null) {
                return -1;
            }
            
            // Find the grid entry and return its ID
            //
            for(int index = 0; index < ipes.Count; ++index) {
                if (ipes[index].Equals(gridEntry)) {
                    return index;
                }
            }
            
            return -1;
        }
 
        internal void AccessibilitySelect(GridEntry entry) {
            SelectGridEntry(entry, true);            
            FocusInternal();
        }
 
        private void AddGridEntryEvents(GridEntryCollection ipeArray, int startIndex, int count) {
            if (ipeArray == null) {
                return;
            }
            
            if (count == -1) {
                count = ipeArray.Count - startIndex;
            }
 
            for (int i= startIndex; i < (startIndex + count); i++) {
                if (ipeArray[i] != null) {
                    GridEntry ge = ipeArray.GetEntry(i);
                    ge.AddOnValueClick(ehValueClick);
                    ge.AddOnLabelClick(ehLabelClick);
                    ge.AddOnOutlineClick(ehOutlineClick);
                    ge.AddOnOutlineDoubleClick(ehOutlineClick);
                    ge.AddOnValueDoubleClick(ehValueDblClick);
                    ge.AddOnLabelDoubleClick(ehLabelDblClick);
                    ge.AddOnRecreateChildren(ehRecreateChildren);
                }
            }
        }
        
        protected virtual void AdjustOrigin(System.Drawing.Graphics g, Point newOrigin, ref Rectangle r) {
            
            Debug.WriteLineIf(GridViewDebugPaint.TraceVerbose,  "Adjusting paint origin to (" + newOrigin.X.ToString(CultureInfo.InvariantCulture) + "," + newOrigin.Y.ToString(CultureInfo.InvariantCulture) + ")");
 
            g.ResetTransform();
            g.TranslateTransform(newOrigin.X, newOrigin.Y);
            r.Offset(-newOrigin.X, -newOrigin.Y);
        }
 
        private void CancelSplitterMove() {
            Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose,  "PropertyGridView:CancelSplitterMove");
            if (GetFlag(FlagIsSplitterMove)) {
                SetFlag(FlagIsSplitterMove, false);
                CaptureInternal = false;
            
                if (selectedRow != -1) {
                    SelectRow(selectedRow);
                }
            }
        }
 
        internal GridPositionData CaptureGridPositionData() {
            return new GridPositionData(this);
        }
 
        private void ClearGridEntryEvents(GridEntryCollection ipeArray, int startIndex, int count) {
            Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose,  "PropertyGridView:ClearGridEntryEvents");
            if (ipeArray == null) {
                return;
            }
            
            if (count == -1) {
                count = ipeArray.Count - startIndex;
            }
 
            for (int i = startIndex ; i < (startIndex + count); i++) {
                if (ipeArray[i] != null) {
                    GridEntry ge = ipeArray.GetEntry(i);
                    ge.RemoveOnValueClick(ehValueClick);
                    ge.RemoveOnLabelClick(ehLabelClick);
                    ge.RemoveOnOutlineClick(ehOutlineClick);
                    ge.RemoveOnOutlineDoubleClick(ehOutlineClick);
                    ge.RemoveOnValueDoubleClick(ehValueDblClick);
                    ge.RemoveOnLabelDoubleClick(ehLabelDblClick);
                    ge.RemoveOnRecreateChildren(ehRecreateChildren);
                }
            }
        }
 
        public void ClearProps() {
            Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose,  "PropertyGridView:ClearProps");
 
 
            if (!HasEntries) {
                return;
            }
 
            CommonEditorHide();
            topLevelGridEntries = null;
            ClearGridEntryEvents(allGridEntries, 0, -1);
            allGridEntries = null;
            selectedRow = -1;
            //selectedGridEntry = null; // we don't wanna clear this because then we can't save where we were on a Refresh()
            tipInfo = -1;
        }
 
        /// <include file='doc\PropertyGridView.uex' path='docs/doc[@for="PropertyGridView.CloseDropDown"]/*' />
        /// <devdoc>
        ///      Closes a previously opened drop down.  This should be called by the
        ///      drop down when the user does something that should close it.
        /// </devdoc>
        public void /* IWindowsFormsEditorService. */ CloseDropDown() {
            CloseDropDownInternal(true);
        }
 
        private void CloseDropDownInternal(bool resetFocus) {
            Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose,  "PropertyGridView:CloseDropDown");
 
            // the activation code in the DropDownHolder can cause this to recurse...
 
            if (GetFlag(FlagDropDownClosing)) {
                return;
            }
            try {
                SetFlag(FlagDropDownClosing, true);
                if (dropDownHolder != null && dropDownHolder.Visible) {
 
                    if (dropDownHolder.Component == DropDownListBox && GetFlag(FlagDropDownCommit)) {
                        OnListClick(null,  null);
                    }
 
                    Edit.Filter = false;
 
                    // disable the ddh so it wont' steal the focus back
                    //
                    dropDownHolder.SetComponent(null, false);
                    dropDownHolder.Visible = false;
 
                    // when we disable the dropdown holder, focus will be lost,
                    // so put it onto one of our children first.
                    if (resetFocus) {
                        if (DialogButton.Visible) {
                            DialogButton.FocusInternal();
                        }
                        else if (DropDownButton.Visible) {
                            DropDownButton.FocusInternal();
                        }
                        else if (Edit.Visible) {
                            Edit.FocusInternal();
                        }
                        else {
                            FocusInternal();
                        }
                    
                        if (selectedRow != -1) {
                            SelectRow(selectedRow);
                        }
                    }
 
                    if (AccessibilityImprovements.Level3 && selectedRow != -1) {
                        var gridEntry = GetGridEntryFromRow(selectedRow);
                        if (gridEntry != null) {
                            gridEntry.AccessibilityObject.RaiseAutomationEvent(NativeMethods.UIA_AutomationFocusChangedEventId);
                            gridEntry.AccessibilityObject.RaiseAutomationPropertyChangedEvent(
                                NativeMethods.UIA_ExpandCollapseExpandCollapseStatePropertyId,
                                UnsafeNativeMethods.ExpandCollapseState.Expanded,
                                UnsafeNativeMethods.ExpandCollapseState.Collapsed);
                        }
                    }
                }
            }
            finally {
                SetFlag(FlagDropDownClosing, false);
            }
        }
 
        private void CommonEditorHide() {
                CommonEditorHide(false);
        }
 
        private void CommonEditorHide(bool always) {
            Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose,  "PropertyGridView:CommonEditorHide");
 
            if (!always && !HasEntries) {
                return;
            }
            
            CloseDropDown();
 
            bool gotfocus = false;
            
            if (Edit.Focused || DialogButton.Focused || DropDownButton.Focused) {
 
                if (IsHandleCreated && Visible && Enabled) {
 
                    gotfocus = IntPtr.Zero != UnsafeNativeMethods.SetFocus(new HandleRef(this, Handle));
                }
            }
            
            try {
               // We do this becuase the Focus call above doesn't always stick, so
               // we make the Edit think that it doesn't have focus.  this prevents
               // ActiveControl code on the containercontrol from moving focus elsewhere
               // when the dropdown closes.
               Edit.DontFocus = true;
               if (Edit.Focused && !gotfocus) {
                 gotfocus = this.FocusInternal();
               }
               Edit.Visible = false;
               
               Edit.SelectionStart = 0;
               Edit.SelectionLength = 0;
               
               if (DialogButton.Focused && !gotfocus) {
                  gotfocus = this.FocusInternal();
               }
               DialogButton.Visible = false;
               
               if (DropDownButton.Focused && !gotfocus) {
                   gotfocus = this.FocusInternal();
               }
               DropDownButton.Visible = false;
               currentEditor = null;
            }
            finally {
               Edit.DontFocus = false;
            }
        }
 
        protected virtual void CommonEditorSetup(Control ctl) {
            Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose,  "PropertyGridView:CommonEditorSetup");
            ctl.Visible = false;
            Controls.Add(ctl);
        }
 
        protected virtual void CommonEditorUse(Control ctl, Rectangle rectTarget) {
 
            Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose,  "PropertyGridView:CommonEditorUse");
            Debug.WriteLineIf(GridViewDebugPaint.TraceVerbose,  "Showing common editors");
 
            Debug.Assert(ctl != null, "Null control passed to CommonEditorUse");
 
            Rectangle rectCur = ctl.Bounds;
 
            // the client rect minus the border line
            Rectangle clientRect = this.ClientRectangle;
 
            clientRect.Inflate(-1,-1);
 
            try {
                rectTarget = Rectangle.Intersect(clientRect, rectTarget);
                 //if (ctl is Button)
                 //   Debug.WriteStackTrace();
 
 
                if (!rectTarget.IsEmpty) {
                    if (!rectTarget.Equals(rectCur)) {
                        ctl.SetBounds(rectTarget.X,rectTarget.Y,
                                      rectTarget.Width,rectTarget.Height);
                    }
                    ctl.Visible = true;
                }
            }
            catch {
                rectTarget = Rectangle.Empty;
            }
 
            if (rectTarget.IsEmpty) {
 
                ctl.Visible = false;
            }
 
            currentEditor = ctl;
 
        }
 
        private /*protected virtual*/ int CountPropsFromOutline(GridEntryCollection rgipes) {
            Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose,  "PropertyGridView:CountPropsFromOutLine");
            if (rgipes == null) return 0;
            int cProps = rgipes.Count;
            for (int i = 0; i < rgipes.Count; i++) {
                if (((GridEntry)rgipes[i]).InternalExpanded)
                    cProps += CountPropsFromOutline(((GridEntry)rgipes[i]).Children);
            }
            return cProps;
        }
 
        /// <include file='doc\PropertyGridView.uex' path='docs/doc[@for="PropertyGridView.CreateAccessibilityInstance"]/*' />
        /// <devdoc>
        ///     Constructs the new instance of the accessibility object for this control. Subclasses
        ///     should not call base.CreateAccessibilityObject.
        /// </devdoc>
        protected override AccessibleObject CreateAccessibilityInstance() {
            return new PropertyGridViewAccessibleObject(this, ownerGrid);
        }
 
        [ResourceExposure(ResourceScope.Machine)]
        [ResourceConsumption(ResourceScope.Machine)]
        private Bitmap CreateResizedBitmap(string icon, int width, int height) {
            Bitmap bitmap = null;
            var scaledIconWidth = width;
            var scaledIconHeight = height;
            try {
                //scale for per-monitor DPI.
                if (DpiHelper.EnableDpiChangedHighDpiImprovements) {
                    scaledIconWidth = LogicalToDeviceUnits(width);
                    scaledIconHeight = LogicalToDeviceUnits(height);
                }
                else if (DpiHelper.IsScalingRequired ) {
                    // only primary monitor scaling.
                    scaledIconWidth = DpiHelper.LogicalToDeviceUnitsX(width);
                    scaledIconHeight = DpiHelper.LogicalToDeviceUnitsY(height);
                }
                
                bitmap = GetBitmapFromIcon(icon, scaledIconWidth, scaledIconHeight);
            }
            catch (Exception e) {
                Debug.Fail(e.ToString());
                bitmap = new Bitmap(scaledIconWidth, scaledIconHeight);
            }
            return bitmap;
        }
 
        
        protected virtual void CreateUI() {
            UpdateUIBasedOnFont(false);
        }
 
        protected override void Dispose(bool disposing) {
            if (disposing) {
                Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose,  "PropertyGridView:Dispose");
                if (scrollBar != null) scrollBar.Dispose();
                if (listBox != null) listBox.Dispose();
                if (dropDownHolder != null) dropDownHolder.Dispose();
                scrollBar = null;
                listBox = null;
                dropDownHolder = null;
 
                ownerGrid = null;
                topLevelGridEntries = null;
                allGridEntries = null;
                serviceProvider = null;
                
                topHelpService = null;
 
                if (helpService != null && helpService is IDisposable)
                    ((IDisposable)helpService).Dispose();
 
                helpService = null;
 
                if (edit != null) {
                    edit.Dispose();
                    edit = null;
                }
 
                if (fontBold != null) {
                    fontBold.Dispose();
                    fontBold = null;
 
                }
 
                if (btnDropDown != null) {
                    btnDropDown.Dispose();
                    btnDropDown = null;
                }
 
                if (btnDialog != null) {
                    btnDialog.Dispose();
                    btnDialog = null;
                }
 
                if (toolTip != null) {
                    toolTip.Dispose();
                    toolTip = null;
                }
            }
 
            base.Dispose(disposing);
        }
        
        public void DoCopyCommand() {
            if (this.CanCopy) {    
               if (Edit.Focused) {
                    Edit.Copy();
               }
               else {
                    Clipboard.SetDataObject(selectedGridEntry.GetPropertyTextValue());           
               }
            }
        }
        
        public void DoCutCommand() {
            if (this.CanCut) {
               DoCopyCommand();
               if (Edit.Visible) {
                    Edit.Cut();
               }
            }
        }
 
 
        public void DoPasteCommand() {
            if (this.CanPaste && Edit.Visible) {
               if (Edit.Focused) {
                    Edit.Paste();
               }
               else {
                   IDataObject dataObj = Clipboard.GetDataObject();
                   if (dataObj != null) {
                      string data = (string)dataObj.GetData(typeof(string));
                      if (data != null) {
                         Edit.FocusInternal();
                         Edit.Text = data;
                         SetCommitError(ERROR_NONE, true);
                      }   
                   }
               }
            }
        }
        
        public void DoUndoCommand() {
            if (this.CanUndo && Edit.Visible) {
               Edit.SendMessage(NativeMethods.WM_UNDO, 0, 0);
            }
        }
        
        internal void DumpPropsToConsole(GridEntry entry, string prefix) { 
        
            Type propType = entry.PropertyType;
            
            if (entry.PropertyValue != null) {
               propType = entry.PropertyValue.GetType();
            }
            
            System.Console.WriteLine(prefix + entry.PropertyLabel + ", value type=" + (propType == null ? "(null)" : propType.FullName) + ", value=" + (entry.PropertyValue == null ? "(null)" : entry.PropertyValue.ToString()) + 
                                     ", flags=" + entry.Flags.ToString(CultureInfo.InvariantCulture) + 
                                     ", TypeConverter=" + (entry.TypeConverter == null ? "(null)" : entry.TypeConverter.GetType().FullName) + ", UITypeEditor=" + ((entry.UITypeEditor == null ? "(null)" : entry.UITypeEditor.GetType().FullName)));
            GridEntryCollection children = entry.Children;
            
            if (children != null) {
               foreach(GridEntry g in children) {
                  DumpPropsToConsole(g, prefix + "\t");
               }
            }
        }
 
        private int GetIPELabelIndent(GridEntry gridEntry) {
            //return OUTLINE_INDENT*(gridEntry.PropertyDepth + 1);
            return gridEntry.PropertyLabelIndent + 1;
        }
 
        private int GetIPELabelLength(System.Drawing.Graphics g, GridEntry gridEntry) {
            SizeF sizeF = PropertyGrid.MeasureTextHelper.MeasureText(this.ownerGrid, g, gridEntry.PropertyLabel, Font);
            Size size = Size.Ceiling(sizeF);
            return ptOurLocation.X + GetIPELabelIndent(gridEntry) + size.Width;
        }
 
        private bool IsIPELabelLong(System.Drawing.Graphics g,GridEntry gridEntry) {
            if (gridEntry == null) return false;
            int length = GetIPELabelLength(g,gridEntry);
            return(length > ptOurLocation.X + InternalLabelWidth);
        }
 
        protected virtual void DrawLabel(System.Drawing.Graphics g, int row, Rectangle rect, bool selected, bool fLongLabelRequest, ref Rectangle clipRect) {
 
            GridEntry gridEntry = GetGridEntryFromRow(row);
 
            if (gridEntry == null || rect.IsEmpty)
                return;
 
            Debug.WriteLineIf(GridViewDebugPaint.TraceVerbose,  "Drawing label for property " + gridEntry.PropertyLabel);
            
            Point newOrigin = new Point(rect.X, rect.Y);
            Rectangle cr = Rectangle.Intersect(rect, clipRect);
            
            if (cr.IsEmpty) {
                return;
            }
            
            AdjustOrigin(g, newOrigin, ref rect);
            cr.Offset(-newOrigin.X, -newOrigin.Y);
            
            try {
                try
                {
                    bool fLongLabel = false;
                    int labelEnd = 0;
                    int labelIndent = GetIPELabelIndent(gridEntry);
 
                    if (fLongLabelRequest)
                    {
                        labelEnd = GetIPELabelLength(g, gridEntry);
                        fLongLabel = IsIPELabelLong(g, gridEntry);
                    }
 
                    gridEntry.PaintLabel(g, rect, cr, selected, fLongLabel);
                }
                catch (Exception ex)
                {
                    Debug.Fail(ex.ToString());
                }
            }
            finally {
                ResetOrigin(g);
            }
        }
 
        protected virtual void DrawValueEntry(System.Drawing.Graphics g, int row, ref Rectangle clipRect) {
            GridEntry gridEntry = GetGridEntryFromRow(row);
            if (gridEntry == null)
                return;
 
            Debug.WriteLineIf(GridViewDebugPaint.TraceVerbose,  "Drawing value for property " + gridEntry.PropertyLabel);
 
            Rectangle r = GetRectangle(row,ROWVALUE);
            Point newOrigin = new Point(r.X, r.Y);
            Rectangle cr = Rectangle.Intersect(clipRect, r);
 
            if (cr.IsEmpty) {
                return;
            }
            
            AdjustOrigin(g, newOrigin, ref r);
            cr.Offset(-newOrigin.X, -newOrigin.Y);
 
            try {
                try {
                    DrawValueEntry(g,r, cr,gridEntry,null, true);
                }
                catch {
                }
            }
            finally {
                ResetOrigin(g);
            }
        }
 
        private /*protected virtual*/ void DrawValueEntry(System.Drawing.Graphics g, Rectangle rect, Rectangle clipRect, GridEntry gridEntry, object value, bool fetchValue) {
            DrawValue(g, rect, clipRect, gridEntry, value, false, true, fetchValue, true);
        }
        private void DrawValue(System.Drawing.Graphics g, Rectangle rect, Rectangle clipRect, GridEntry gridEntry, object value, bool drawSelected, bool checkShouldSerialize, bool fetchValue, bool paintInPlace) { 
            GridEntry.PaintValueFlags paintFlags = GridEntry.PaintValueFlags.None;
 
            if (drawSelected) {
                paintFlags |= GridEntry.PaintValueFlags.DrawSelected;
            }
 
            if (checkShouldSerialize) {
               paintFlags |= GridEntry.PaintValueFlags.CheckShouldSerialize;
            }
 
            if (fetchValue) {
                paintFlags |= GridEntry.PaintValueFlags.FetchValue;
            }
 
            if (paintInPlace) {
                paintFlags |= GridEntry.PaintValueFlags.PaintInPlace;
            }
 
            gridEntry.PaintValue(value, g, rect, clipRect, paintFlags);
        }
        
        private void F4Selection(bool popupModalDialog) {
            GridEntry gridEntry = GetGridEntryFromRow(selectedRow);
            if (gridEntry == null) return;
 
            // if we are in an errorState, just put the focus back on the Edit
            if (errorState != ERROR_NONE && Edit.Visible) {
                Edit.FocusInternal();
                return;
            }
 
            if (DropDownButton.Visible) {
                PopupDialog(selectedRow);
            }
            else if (DialogButton.Visible) {
                if (popupModalDialog) {
                    PopupDialog(selectedRow);
                }
                else {
                    DialogButton.FocusInternal();
                }
            }
            else if (Edit.Visible) {
                Edit.FocusInternal();
                SelectEdit(false);
            }
            return;
        }
 
        //The following Suppress message calls are required so we can handle
        //errors when attempting to generate an event handler when the document
        //is read-only.  This code is a duplicate of the error handling in 
        //CommitValue().
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2201:DoNotRaiseReservedExceptionTypes")]            
        public void DoubleClickRow(int row, bool toggleExpand, int type) {
            Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose,  "PropertyGridView:DoubleClickRow");
            GridEntry gridEntry = GetGridEntryFromRow(row);
            if (gridEntry == null) return;
 
            Debug.WriteLineIf(GridViewDebugPaint.TraceVerbose,  "Property " + gridEntry.PropertyLabel + " double clicked");
 
            if (!toggleExpand || type == ROWVALUE) {
                try {
                    bool action = gridEntry.DoubleClickPropertyValue();
                    if (action) {
                        SelectRow(row);
                        return;
                    }
                }
                catch (Exception ex) {
                    SetCommitError(ERROR_THROWN);
                    ShowInvalidMessage(gridEntry.PropertyLabel, null, ex);
                    return;
                }
            }
 
            SelectGridEntry(gridEntry, true);
 
            if (type == ROWLABEL && toggleExpand && gridEntry.Expandable) {
                SetExpand(gridEntry,!gridEntry.InternalExpanded);
                return;
            }
            
            if (gridEntry.IsValueEditable && gridEntry.Enumerable) {
                int index = GetCurrentValueIndex(gridEntry);
 
                if (index != -1) {
                    object[] values = gridEntry.GetPropertyValueList();
 
                    if (values == null || index >= (values.Length - 1)) {
                        index = 0;
                    }
                    else {
                        index++;
                    }
 
                    CommitValue(values[index]);
                    SelectRow(selectedRow);
                    Refresh();
                    return;
                }
            }
        
            if (Edit.Visible) {
                Edit.FocusInternal();
                SelectEdit(false);
                return;
            }
        }
 
        public Font GetBaseFont() {
            return Font;
        }
 
        public Font GetBoldFont() {
            if (fontBold == null) {
                fontBold = new Font(this.Font, FontStyle.Bold);
            }
            return fontBold;
        }
 
        internal IntPtr GetBaseHfont() {
            if (baseHfont == IntPtr.Zero) {
                baseHfont = GetBaseFont().ToHfont();
            }
            return baseHfont;
        }
 
        /// <summary>
        /// Gets the element from point.
        /// </summary>
        /// <param name="x">The point x coordinate.</param>
        /// <param name="y">The point y coordinate.</param>
        /// <returns>The found grid element.</returns>
        internal GridEntry GetElementFromPoint(int x, int y) {
            Point point = new Point(x, y);
            var allGridEntries = GetAllGridEntries();
            GridEntry[] targetEntries = new GridEntry[allGridEntries.Count];
            try {
                GetGridEntriesFromOutline(allGridEntries, 0, allGridEntries.Count - 1, targetEntries);
            }
            catch (Exception ex) {
                Debug.Fail(ex.ToString());
            }
 
            foreach (GridEntry gridEntry in targetEntries) {
                if (gridEntry.AccessibilityObject.Bounds.Contains(point)) {
                    return gridEntry;
                }
            }
 
            return null;
        }
 
        internal IntPtr GetBoldHfont() {
            if (boldHfont == IntPtr.Zero) {
                boldHfont = GetBoldFont().ToHfont();
            }
            return boldHfont;
        }
 
 
        private bool GetFlag(short flag) {
            return (this.flags & flag) != 0;
        }
 
        public virtual Color GetLineColor() {
            return ownerGrid.LineColor;
        }
 
        public virtual Brush GetLineBrush(Graphics g) {
            if (ownerGrid.lineBrush == null) {
                Color clr = g.GetNearestColor(ownerGrid.LineColor);
                ownerGrid.lineBrush = new SolidBrush(clr);
            }
            return ownerGrid.lineBrush;
        }
 
        public virtual Color GetSelectedItemWithFocusForeColor()
        {
            return ownerGrid.SelectedItemWithFocusForeColor;
        }
 
        public virtual Color GetSelectedItemWithFocusBackColor()
        {
            return ownerGrid.SelectedItemWithFocusBackColor;
        }
 
        public virtual Brush GetSelectedItemWithFocusBackBrush(Graphics g)
        {
            if (ownerGrid.selectedItemWithFocusBackBrush == null) {
                Color clr = g.GetNearestColor(ownerGrid.SelectedItemWithFocusBackColor);
                ownerGrid.selectedItemWithFocusBackBrush = new SolidBrush(clr);
            }
            return ownerGrid.selectedItemWithFocusBackBrush;
        }
 
        public virtual IntPtr GetHostHandle() {
            return Handle;
        }
 
        public virtual int GetLabelWidth() {
            return InternalLabelWidth;
        }
 
        internal bool IsExplorerTreeSupported {
            get {
                if (ownerGrid.CanShowVisualStyleGlyphs && UnsafeNativeMethods.IsVista && VisualStyleRenderer.IsSupported) {
                    return true;
                }
 
                return false;
            }
        }
 
        public virtual int GetOutlineIconSize() {
            if (IsExplorerTreeSupported) {
                return outlineSizeExplorerTreeStyle;
            }
            else {
                return outlineSize;
            }
        }
 
        public virtual int GetGridEntryHeight() {
            return RowHeight;
        }
 
        // for qa automation
        internal int GetPropertyLocation(string propName, bool getXY, bool rowValue) {
            if (allGridEntries != null && allGridEntries.Count > 0) {
                for (int i = 0; i < allGridEntries.Count; i++) {
                    if (0 == String.Compare(propName, allGridEntries.GetEntry(i).PropertyLabel, true, CultureInfo.InvariantCulture)) {
                        if (getXY) {
                            int row = GetRowFromGridEntry(allGridEntries.GetEntry(i));
 
                            if (row < 0 || row >= this.visibleRows) {
                                return -1;
                            }
                            else {
                                Rectangle r = GetRectangle(row, rowValue ? ROWVALUE : ROWLABEL);
                                return(r.Y << 16 | (r.X & 0xFFFF));
                            }
                        }
                        else {
                            return i;
                        }
                    }
                }
            }
            return -1;
        }
 
        public new object GetService(Type classService) {
            if (classService == typeof(IWindowsFormsEditorService)) {
                return this;
            }
            if (ServiceProvider != null) {
                return serviceProvider.GetService(classService);
            }
            return null;
        }
 
        public virtual int GetSplitterWidth() {
            return 1;
        }
 
        public virtual int GetTotalWidth() {
            return GetLabelWidth() + GetSplitterWidth() + GetValueWidth();
        }
 
        public virtual int GetValuePaintIndent() {
            return paintIndent;
        }
 
        public virtual int GetValuePaintWidth() {
            return paintWidth;
        }
 
        public virtual int GetValueStringIndent() {
            return EDIT_INDENT;
        }
 
        public virtual int GetValueWidth() {
            return(int)(InternalLabelWidth * (labelRatio - 1));
        }
 
        private void SetDropDownWindowPosition(Rectangle rect, bool setBounds = false) {
             Size size = dropDownHolder.Size;
            
            // Setting size to the width of selected row value field at the minimum.
            size.Width = Math.Max(rect.Width + 1, size.Width);
 
            Point loc = PointToScreen(new Point(0, 0));
            Rectangle rectScreen = Screen.FromControl(Edit).WorkingArea;
 
            loc.X = Math.Min(rectScreen.X + rectScreen.Width - size.Width,
                Math.Max(rectScreen.X, loc.X + rect.X + rect.Width - size.Width));
            loc.Y += rect.Y;
            if (rectScreen.Y + rectScreen.Height < (size.Height + loc.Y + Edit.Height)) {
                loc.Y -= size.Height;
                dropDownHolder.ResizeUp = true;
            }
            else {
                loc.Y += rect.Height + 1;
                dropDownHolder.ResizeUp = false;
            }
 
            int flags = NativeMethods.SWP_NOZORDER | NativeMethods.SWP_NOACTIVATE;
 
            if (loc.X == 0 && loc.Y == 0) {
                flags |= NativeMethods.SWP_NOMOVE;
            }
 
            if (this.Width == size.Width && this.Height == size.Height) {
                flags |= NativeMethods.SWP_NOSIZE;
            }
 
            SafeNativeMethods.SetWindowPos(new HandleRef(this.dropDownHolder, this.dropDownHolder.Handle), NativeMethods.NullHandleRef, loc.X, loc.Y, size.Width, size.Height, flags);
            if (setBounds) {
                dropDownHolder.SetBounds(loc.X, loc.Y, size.Width, size.Height);
            }
        }
 
        /// <include file='doc\PropertyGridView.uex' path='docs/doc[@for="PropertyGridView.DropDownControl"]/*' />
        /// <devdoc>
        ///      Displays the provided control in a drop down.  When possible, the
        ///      current dimensions of the control will be respected.  If this is not possible
        ///      for the current screen layout the control may be resized, so it should
        ///      be implemented using appropriate docking and anchoring so it will resize
        ///      nicely.  If the user performs an action that would cause the drop down
        ///      to prematurely disappear the control will be hidden.
        /// </devdoc>
        public void /* cpr IWindowsFormsEditorService. */ DropDownControl(Control ctl) {
            Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose,  "PropertyGridView:DropDownControl");
            Debug.WriteLineIf(GridViewDebugPaint.TraceVerbose,  "DropDownControl(ctl = " + ctl.GetType().Name + ")");
            if (dropDownHolder == null) {
                dropDownHolder = new DropDownHolder(this);
            }
 
            dropDownHolder.Visible = false;
            if (DpiHelper.EnableDpiChangedHighDpiImprovements) {
                Rectangle rect = GetRectangle(selectedRow, ROWVALUE);
 
                dropDownHolder.SuspendAllLayout(dropDownHolder);
                // Parenting the dropdown holder - may cause WM_DPI changed event
                UnsafeNativeMethods.SetWindowLong(new HandleRef(dropDownHolder, dropDownHolder.Handle), NativeMethods.GWL_HWNDPARENT, new HandleRef(this, Handle));
 
                // WMDPI changed events are raised when parent DPi changed or windows is positioned on a screen with different Dpi than primary monitor.
                // So order of the parenting and positioning of the window need to be in synch to avoid repetitive WM_DPI changed messages.
                SetDropDownWindowPosition(rect);
 
                dropDownHolder.SetComponent(ctl, GetFlag(FlagResizableDropDown));
 
                //Set window position but not bounds to avoid moving window to primary monitor ( set bounds cause it ).
                SetDropDownWindowPosition(rect);
                dropDownHolder.ResumeAllLayout(dropDownHolder,true);
 
                // control is a top=level window. standard way of setparent on the control is prohibited for top-level controls.
                // It is unknown why this control was created as a top-level control. Windows does not recommend this way of setting parent.
                // We are not touching this for this release. We may revisit it in next release.
                SafeNativeMethods.ShowWindow(new HandleRef(dropDownHolder, dropDownHolder.Handle), NativeMethods.SW_SHOWNA);
                SetDropDownWindowPosition(rect, setBounds: true);
            }
            else {
                dropDownHolder.SetComponent(ctl, GetFlag(FlagResizableDropDown));
                Rectangle rect = GetRectangle(selectedRow, ROWVALUE);
                Size size = dropDownHolder.Size;
                Point loc = PointToScreen(new Point(0, 0));
                Rectangle rectScreen = Screen.FromControl(Edit).WorkingArea;
                size.Width = Math.Max(rect.Width + 1, size.Width);
 
                // Not needed... CYMAXDDLHEIGHT used to be 200, but why limit it???
                //size.Height = Math.Min(size.Height,CYMAXDDLHEIGHT);
 
                loc.X = Math.Min(rectScreen.X + rectScreen.Width - size.Width,
                    Math.Max(rectScreen.X, loc.X + rect.X + rect.Width - size.Width));
                loc.Y += rect.Y;
                if (rectScreen.Y + rectScreen.Height < (size.Height + loc.Y + Edit.Height)) {
                    loc.Y -= size.Height;
                    dropDownHolder.ResizeUp = true;
                }
                else {
                    loc.Y += rect.Height + 1;
                    dropDownHolder.ResizeUp = false;
                }
 
                // control is a top=level window. standard way of setparent on the control is prohibited for top-level controls.
                // It is unknown why this control was created as a top-level control. Windows does not recommend this way of setting parent.
                // We are not touching this for this relase. We may revisit it in next release.
                UnsafeNativeMethods.SetWindowLong(new HandleRef(dropDownHolder, dropDownHolder.Handle), NativeMethods.GWL_HWNDPARENT, new HandleRef(this, Handle));
                dropDownHolder.SetBounds(loc.X, loc.Y, size.Width, size.Height);
                SafeNativeMethods.ShowWindow(new HandleRef(dropDownHolder, dropDownHolder.Handle), NativeMethods.SW_SHOWNA);
            }
 
            Edit.Filter = true;
            dropDownHolder.Visible = true;
            dropDownHolder.FocusComponent();
            SelectEdit(false);
 
            if (AccessibilityImprovements.Level3) {
                var gridEntry = GetGridEntryFromRow(selectedRow);
                if (gridEntry != null) {
                    gridEntry.AccessibilityObject.RaiseAutomationEvent(NativeMethods.UIA_AutomationFocusChangedEventId);
                    gridEntry.AccessibilityObject.RaiseAutomationPropertyChangedEvent(
                        NativeMethods.UIA_ExpandCollapseExpandCollapseStatePropertyId,
                        UnsafeNativeMethods.ExpandCollapseState.Collapsed,
                        UnsafeNativeMethods.ExpandCollapseState.Expanded);
                }
            }
 
            try {
                DropDownButton.IgnoreMouse = true;
                dropDownHolder.DoModalLoop();
            }
            finally {
                DropDownButton.IgnoreMouse = false;
            }
 
            if (selectedRow != -1) {
                FocusInternal();
                SelectRow(selectedRow);
            }
        }
 
        public virtual void DropDownDone() {
            CloseDropDown();
        }
 
        public virtual void DropDownUpdate() {
            Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose,  "DropDownHolder:DropDownUpdate");
            if (dropDownHolder != null && dropDownHolder.GetUsed()) {
                int row = selectedRow;
                GridEntry gridEntry = GetGridEntryFromRow(row);
                Edit.Text = gridEntry.GetPropertyTextValue();
            }
        }
        
        public bool EnsurePendingChangesCommitted() {
            this.CloseDropDown();
            return this.Commit();
        }
        
        private bool FilterEditWndProc(ref Message m) {
            // if it's the TAB key, we keep it since we'll give them focus with it.
            if (dropDownHolder != null && dropDownHolder.Visible && m.Msg == NativeMethods.WM_KEYDOWN && (int)m.WParam != (int)Keys.Tab) {
                Control ctl = dropDownHolder.Component;
                if (ctl != null) {
                    m.Result = ctl.SendMessage(m.Msg, m.WParam, m.LParam);
                    return true;
                }
            }
            return false;
        }
 
        private bool FilterReadOnlyEditKeyPress(char keyChar) {
            GridEntry gridEntry = GetGridEntryFromRow(selectedRow);
            if (gridEntry.Enumerable && gridEntry.IsValueEditable) {
                int index = GetCurrentValueIndex(gridEntry);
 
                object[] values = gridEntry.GetPropertyValueList();
                string letter = new string(new char[] {keyChar});
                for (int i = 0; i < values.Length; i++) {
                    object valueCur = values[(i + index + 1) % values.Length];
                    string text = gridEntry.GetPropertyTextValue(valueCur);
                    if (text != null && text.Length > 0 && String.Compare(text.Substring(0,1), letter, true, CultureInfo.InvariantCulture) == 0) {
                        CommitValue(valueCur);
                        if (Edit.Focused) {
                            SelectEdit(false);
                        }
                        return true;
                    }
                }
 
            }
            return false;
        }
 
        public virtual bool WillFilterKeyPress(char charPressed) {
            if (!Edit.Visible) {
                return false;
            }
 
            Keys modifiers = ModifierKeys;
            if ((int)(modifiers & ~Keys.Shift) != 0) {
                return false;
            }
 
            // try to activate the Edit.
            // we don't activate for +,-, or * on expandable items because they have special meaning
            // for the tree.
            //
 
            if (selectedGridEntry != null) {
                switch (charPressed) {
                    case '+':
                    case '-':
                    case '*':
                        return !selectedGridEntry.Expandable;
                    case unchecked( (char)(int)(long)Keys.Tab):
                        return false;
                }
            }
 
            return true;
        }
 
        public void FilterKeyPress(char keyChar) {
 
            GridEntry gridEntry = GetGridEntryFromRow(selectedRow);
            if (gridEntry == null)
                return;
 
            Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose,  "PropertyGridView:FilterKeyPress()");
 
            Edit.FilterKeyPress(keyChar);
        }
 
        private /*protected virtual*/ GridEntry FindEquivalentGridEntry(GridEntryCollection ipeHier) {
            if (ipeHier == null || ipeHier.Count == 0)
                return null;
            GridEntryCollection rgipes = GetAllGridEntries();
 
            if (rgipes == null || rgipes.Count == 0) {
                return null;
            }
 
            GridEntry targetEntry = null;
            int row = 0;
            int count = rgipes.Count;
 
            for (int i = 0; i < ipeHier.Count; i++) {
 
                if (ipeHier[i] == null) {
                    continue;
                }
 
                // if we've got one above, and it's expandable,
                // expand it
                if (targetEntry != null) {
 
                    // how many do we have?
                    int items = rgipes.Count;
 
                    // expand and get the new count
                    if (!targetEntry.InternalExpanded) {
                      SetExpand(targetEntry, true);                      
                      rgipes = GetAllGridEntries();                     
                    }
                    count = targetEntry.VisibleChildCount;
                }
 
                int start = row;
                targetEntry = null;
 
                // now, we will only go as many as were expanded...
                for (; row < rgipes.Count && ((row - start) <= count); row++) {
                    if (ipeHier.GetEntry(i).NonParentEquals(rgipes[row])) {
                        targetEntry = rgipes.GetEntry(row);
                        row++;
                        break;
                    }
                }
 
                // didn't find it...
                if (targetEntry == null) {
                    break;
                }
            }
 
            return targetEntry;
        }
 
        protected virtual Point FindPosition(int x, int y) {
            if (RowHeight == -1)
                return InvalidPosition;
            Size size = this.GetOurSize();
 
            if (x < 0 || x > size.Width + ptOurLocation.X)
                return InvalidPosition;
            Point pt = new Point(ROWLABEL,0);
            if (x > InternalLabelWidth + ptOurLocation.X)
                pt.X = ROWVALUE;
            pt.Y = (y-ptOurLocation.Y)/(1+RowHeight);
            return pt;
        }
 
        public virtual void Flush() {
            Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose,  "PropertyGridView::Flush()");
            if (Commit() && Edit.Focused) {
                this.FocusInternal();
            }
        }
 
        private GridEntryCollection GetAllGridEntries() {
            return GetAllGridEntries(false);
        }
 
        private GridEntryCollection GetAllGridEntries(bool fUpdateCache) {
            if (visibleRows == -1 || totalProps == -1 || !HasEntries) {
                return null;
            }
 
            if (allGridEntries != null && !fUpdateCache) {
                return allGridEntries;
            }
 
            GridEntry[] rgipes = new GridEntry[totalProps];
            try
            {
                GetGridEntriesFromOutline(topLevelGridEntries, 0, 0, rgipes);
            }
            catch (Exception ex)
            {
                Debug.Fail(ex.ToString());
            }
            allGridEntries = new GridEntryCollection(null, rgipes);
            AddGridEntryEvents(allGridEntries, 0, -1);
            return allGridEntries;
        }
 
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
        private int GetCurrentValueIndex(GridEntry gridEntry) {
 
            if (!gridEntry.Enumerable) {
                return -1;
            }
 
            try {
                object[] values = gridEntry.GetPropertyValueList();
                object value = gridEntry.PropertyValue;
                string textValue = gridEntry.TypeConverter.ConvertToString(gridEntry, value);
 
                if (values != null && values.Length > 0) {
                    string itemTextValue;
                    int stringMatch = -1;
                    int equalsMatch = -1;
                    for (int i = 0; i < values.Length; i++) {
 
                        object curValue = values[i];
 
                        // check real values against string values.
                        itemTextValue = gridEntry.TypeConverter.ConvertToString(curValue);
                        if (value == curValue || 0 == String.Compare(textValue, itemTextValue, true, CultureInfo.InvariantCulture)) {
                            stringMatch = i;
                        }
                        // now try .equals if they are both non-null
                        if (value != null && curValue != null && curValue.Equals(value)) {
                            equalsMatch = i;
                        }
 
                        if (stringMatch == equalsMatch && stringMatch != -1) {
                            return stringMatch;
                        }
                    }
 
                    if (stringMatch != -1) {
                        return stringMatch;
                    }
 
                    if (equalsMatch != -1) {
                        return equalsMatch;
                    }
                }
            }
            catch (Exception e) {
                Debug.Fail(e.ToString());
            }
            return -1;
 
        }
 
        public virtual int GetDefaultOutlineIndent() {
            return OUTLINE_INDENT;
        }
 
        private IHelpService GetHelpService() {
            if (helpService == null && ServiceProvider != null) {
                topHelpService = (IHelpService)ServiceProvider.GetService(typeof(IHelpService));
                if (topHelpService != null) {
                     IHelpService localHelpService = topHelpService.CreateLocalContext(HelpContextType.ToolWindowSelection);
                     if (localHelpService != null) {
                        helpService = localHelpService;
                     }
                }
            }
            return helpService;
        }
 
        public virtual int GetScrollOffset() {
            //Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose,  "PropertyGridView:GetScrollOffset");
            if (scrollBar == null) {
                return 0;
            }
            int pos = ScrollBar.Value;
            return pos;
        }
 
        /// <include file='doc\PropertyGridView.uex' path='docs/doc[@for="PropertyGridView.GetGridEntryHierarchy"]/*' />
        /// <devdoc>
        /// returns an array of IPE specifying the current heirarchy of ipes from the given
        /// gridEntry through its parents to the root.
        /// </devdoc>
        private GridEntryCollection GetGridEntryHierarchy(GridEntry gridEntry) {
            if (gridEntry == null) {
                return null;
            }
 
            int depth = gridEntry.PropertyDepth;
            if (depth > 0) {
                GridEntry[] entries = new GridEntry[depth + 1];
 
                while (gridEntry != null && depth >= 0) {
                    entries[depth] = gridEntry;
                    gridEntry = gridEntry.ParentGridEntry;
                    depth = gridEntry.PropertyDepth;
                }
                return new GridEntryCollection(null, entries);
            }
            return new GridEntryCollection(null, new GridEntry[]{gridEntry});
        }
 
        private /*protected virtual*/ GridEntry GetGridEntryFromRow(int row) {
            return GetGridEntryFromOffset(row + GetScrollOffset());
        }
 
        private /*protected virtual*/ GridEntry GetGridEntryFromOffset(int offset) {
            GridEntryCollection rgipesAll = GetAllGridEntries();
            if (rgipesAll != null) {
                if (offset >= 0 && offset < rgipesAll.Count)
                    return rgipesAll.GetEntry(offset);
            }
            return null;
        }
 
        private /*protected virtual*/ int GetGridEntriesFromOutline(GridEntryCollection rgipe, int cCur,
                                                 int cTarget, GridEntry[] rgipeTarget) {
            Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose,  "PropertyGridView:GetGridEntriesFromOutline");
            if (rgipe == null || rgipe.Count == 0)
                return cCur;
 
            cCur--; // want to account for each entry as we find it.
 
            for (int cLocal = 0; cLocal < rgipe.Count; cLocal++) {
                cCur++;
                if (cCur >= cTarget + rgipeTarget.Length)
                    break;
 
                GridEntry ipeCur = rgipe.GetEntry(cLocal);
 
                //Debug.Assert(ipeCur != null, "Null IPE at position " + cLocal.ToString());
 
 
                if (cCur >= cTarget)
                    rgipeTarget[cCur - cTarget] = ipeCur;
 
                if (ipeCur.InternalExpanded) {
                    GridEntryCollection subGridEntry = ipeCur.Children;
                    //Debug.Assert(subGridEntry != null && subGridEntry.Length > 0 && subGridEntry[0] != null, "Expanded property " + ipeCur.PropertyLabel + " has no children!");
                    if (subGridEntry != null && subGridEntry.Count > 0) {
                        cCur = GetGridEntriesFromOutline(subGridEntry,
                                                  cCur+1,cTarget,rgipeTarget);
                    }
                }
            }
 
            return cCur;
        }
 
        private Size GetOurSize() {
                Size size = ClientSize;
                if (size.Width == 0) {
                    Size sizeWindow = Size;
                    if (sizeWindow.Width > 10) {
                        Debug.Fail("We have a bad client width!");
                        size.Width = sizeWindow.Width;
                        size.Height = sizeWindow.Height;
                    }
                }
                if (!GetScrollbarHidden()) {
                    Size sizeScroll = ScrollBar.Size;
                    size.Width -= sizeScroll.Width;
                }
                size.Width -= 2;
                size.Height -= 2;
                return size;
        }
 
        public Rectangle GetRectangle(int row, int flRow) {
            Rectangle rect = new Rectangle(0,0,0,0);
            Size size = this.GetOurSize();
            
            rect.X = ptOurLocation.X;
 
            bool fLabel = ((flRow & ROWLABEL) != 0);
            bool fValue = ((flRow & ROWVALUE) != 0);
 
            if (fLabel && fValue) {
                rect.X = 1;
                rect.Width = size.Width - 1;
            }
            else if (fLabel) {
                rect.X = 1;
                rect.Width = InternalLabelWidth - 1;
            }
            else if (fValue) {
                rect.X = ptOurLocation.X + InternalLabelWidth;
                rect.Width = size.Width - InternalLabelWidth;
            }
 
            rect.Y = (row)*(RowHeight+1)+1+ptOurLocation.Y;
            rect.Height = RowHeight;
 
            return rect;
        }
 
        private int GetRowFromGridEntry(GridEntry gridEntry) {
            GridEntryCollection rgipesAll = GetAllGridEntries();
            if (gridEntry == null || rgipesAll == null)
                return -1;
 
            int bestMatch = -1;
 
            for (int i = 0; i < rgipesAll.Count; i++) {
 
                // try for an exact match.  semantics of equals are a bit loose here...
                //
                if (gridEntry == rgipesAll[i]) {
                    return i - GetScrollOffset();
                }
                else if (bestMatch == -1 && gridEntry.Equals(rgipesAll[i])) {
                    bestMatch = i - GetScrollOffset();
                }
            }
 
            if (bestMatch != -1) {
                return bestMatch;
            }
 
            return -1 - GetScrollOffset();
        }
 
        internal int GetRowFromGridEntryInternal(GridEntry gridEntry) {
            return GetRowFromGridEntry(gridEntry);
        }
 
        public virtual bool GetInPropertySet() {
            return GetFlag(FlagInPropertySet);
        }
 
        protected virtual bool GetScrollbarHidden() {
            if (scrollBar == null) {
                return true;
            }
            return !ScrollBar.Visible;
        }
 
        /// <include file='doc\PropertyGridView.uex' path='docs/doc[@for="PropertyGridView.GetTestingInfo"]/*' />
        /// <devdoc>
        /// Returns a string containing test info about a given GridEntry. Requires an offset into the top-level
        /// entry collection (ie. nested entries are not accessible). Or specify -1 to get info for the current
        /// selected entry (which can be any entry, top-level or nested).
        /// </devdoc>
        public virtual string GetTestingInfo(int entry) {
            GridEntry gridEntry = (entry < 0) ? GetGridEntryFromRow(selectedRow) : GetGridEntryFromOffset(entry);
 
            if (gridEntry == null)
                return "";
            else
                return gridEntry.GetTestingInfo();
        }
 
        public Color GetTextColor() {
            return this.ForeColor;
        }
 
        private void LayoutWindow(bool invalidate) {
            Rectangle rect = ClientRectangle;
            Size sizeWindow = new Size(rect.Width,rect.Height);
 
            if (scrollBar != null) {
                Rectangle boundsScroll = ScrollBar.Bounds;
                boundsScroll.X = sizeWindow.Width - boundsScroll.Width - 1;
                boundsScroll.Y = 1;
                boundsScroll.Height = sizeWindow.Height - 2;
                ScrollBar.Bounds = boundsScroll;
            }
 
            if (invalidate) {
                Invalidate();
            }
        }
 
        internal void InvalidateGridEntryValue(GridEntry ge) {
            int row = GetRowFromGridEntry(ge);
            if (row != -1) {
                InvalidateRows(row, row, ROWVALUE);
            }
        }
 
        private void InvalidateRow(int row) {
            InvalidateRows(row, row, ROWVALUE | ROWLABEL);
        }
 
 
        private void InvalidateRows(int startRow, int endRow) {
            InvalidateRows(startRow, endRow, ROWVALUE | ROWLABEL);
        }
 
        private void InvalidateRows(int startRow, int endRow, int type) {
            Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose,  "PropertyGridView:InvalidateRows");
 
            Debug.WriteLineIf(GridViewDebugPaint.TraceVerbose,  "Invalidating rows " + startRow.ToString(CultureInfo.InvariantCulture) + " through " + endRow.ToString(CultureInfo.InvariantCulture));
            Rectangle rect;
 
            // invalidate from the start row down
            if (endRow == -1) {
                rect = GetRectangle(startRow, type);
                rect.Height = (Size.Height - rect.Y) - 1;
                Invalidate(rect);
            }
            else {
                for (int i = startRow; i <= endRow; i++) {
                    rect = GetRectangle(i, type);
                    Invalidate(rect);
                }
            }
        }
 
     
        /// <include file='doc\PropertyGridView.uex' path='docs/doc[@for="PropertyGridView.IsInputKey"]/*' />
        /// <devdoc>
        ///     Overridden to handle TAB key.
        /// </devdoc>
        protected override bool IsInputKey(Keys keyData) {
            switch (keyData & Keys.KeyCode) {
                case Keys.Escape:
                case Keys.Tab:
                case Keys.F4:
                    return false;
 
                case Keys.Return:
                     if (Edit.Focused) {
                        return false;
                     }
                     break;
            }
            return base.IsInputKey(keyData);
        }
 
        private bool IsMyChild(Control c) {
 
            if (c == this || c == null) {
                return false;
            }
 
            Control cParent = c.ParentInternal;
 
            while (cParent != null) {
                if (cParent == this) {
                    return true;
                }
                cParent = cParent.ParentInternal;
            }
            return false;
        }
 
        private bool IsScrollValueValid(int newValue) {
            /*Debug.WriteLine("se.newValue = " + se.newValue.ToString());
            Debug.WriteLine("ScrollBar.Value = " + ScrollBar.Value.ToString());
            Debug.WriteLine("visibleRows = " + visibleRows.ToString());
            Debug.WriteLine("totalProps = " + totalProps.ToString());
            Debug.WriteLine("ScrollBar.Max = " + ScrollBar.Maximum.ToString());
            Debug.WriteLine("ScrollBar.LargeChange = " + ScrollBar.LargeChange.ToString());*/
 
            // is this move valid?
            if (newValue == ScrollBar.Value ||
                newValue < 0 ||
                newValue > ScrollBar.Maximum ||
                (newValue + (ScrollBar.LargeChange-1) >= totalProps)) {
                Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose,  "PropertyGridView: move not needed, returning");
                return false;
            }
            return true;
        }
 
        internal bool IsSiblingControl(Control c1, Control c2) {
 
            Control parent1 = c1.ParentInternal;
            Control parent2 = c2.ParentInternal;
 
            while (parent2 != null) {
                if (parent1 == parent2) {
                    return true;
                }
                parent2 = parent2.ParentInternal;
            }
            return false;
        }
 
        private void MoveSplitterTo(int xpos) {
 
            int widthPS = GetOurSize().Width;
            int startPS = ptOurLocation.X;
            int pos = Math.Max(Math.Min(xpos,widthPS-10),GetOutlineIconSize() * 2);
 
            int oldLabelWidth = InternalLabelWidth;
 
            labelRatio = ((double)widthPS / (double) (pos - startPS));
 
            SetConstants();
 
            if (selectedRow != -1) {
                // do this to move any editor we have
                SelectRow(selectedRow);
            }
            
            Rectangle r = ClientRectangle;
            
            // if we're moving to the left, just invalidate the values
            if (oldLabelWidth > InternalLabelWidth) {
                int left = InternalLabelWidth - requiredLabelPaintMargin;
                Invalidate(new Rectangle(left, 0, Size.Width - left, Size.Height));
            }
            else {
                // to the right, just invalidate from where the splitter was
                // to the right
                r.X = oldLabelWidth - requiredLabelPaintMargin;
                r.Width -= r.X;
                Invalidate(r);
            }
        }
 
        private void OnBtnClick(object sender, EventArgs e) {
 
            if (GetFlag(FlagBtnLaunchedEditor)) {
                return;
            }
 
            if (sender == DialogButton && !Commit()) {
                return;
            }
            SetCommitError(ERROR_NONE);
 
            try {
                Commit();
                SetFlag(FlagBtnLaunchedEditor, true);
                PopupDialog(selectedRow);
            }
            finally {
                SetFlag(FlagBtnLaunchedEditor, false);
            }
        }
 
        private void OnBtnKeyDown(object sender, KeyEventArgs ke) {
            OnKeyDown(sender,ke);
        }
 
 
        private void OnChildLostFocus(object sender, EventArgs e) {
            Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose,  "PropertyGridView:OnChildLostFocus");
            this.InvokeLostFocus(this, e);
        }
 
        private void OnDropDownButtonGotFocus(object sender, EventArgs e) {
            if (AccessibilityImprovements.Level3) {
                DropDownButton dropDownButton = sender as DropDownButton;
                if (dropDownButton != null) {
                    dropDownButton.AccessibilityObject.SetFocus();
                }
            }
        }
 
        protected override void OnGotFocus(EventArgs e) {
            Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose,  "PropertyGridView:OnGotFocus");
            
            base.OnGotFocus(e);
            
            if (e != null && !GetInPropertySet()) {
                if (!Commit()) {
                    Edit.FocusInternal();
                    return;
                }
            }
 
            if (selectedGridEntry != null && GetRowFromGridEntry(selectedGridEntry) != -1) {
                selectedGridEntry.Focus = true;
                SelectGridEntry(selectedGridEntry, false);
            }
            else {
                SelectRow(0);
            }
 
            if (selectedGridEntry != null && selectedGridEntry.GetValueOwner() != null) {
                UpdateHelpAttributes(null, selectedGridEntry);
            }
 
            // For empty GridView, draw a focus-indicator rectangle, just inside GridView borders
            if ((totalProps <= 0) && AccessibilityImprovements.Level1) {
                int doubleOffset = 2 * offset_2Units;
 
                if ((Size.Width > doubleOffset) && (Size.Height > doubleOffset)) {
                    using (Graphics g = CreateGraphicsInternal()) {
                        ControlPaint.DrawFocusRectangle(g, new Rectangle(offset_2Units, offset_2Units, Size.Width - doubleOffset, Size.Height - doubleOffset));
                    }
                }
            }
        }
 
         protected override void OnHandleCreated(EventArgs e) {
            base.OnHandleCreated(e);
            SystemEvents.UserPreferenceChanged += new UserPreferenceChangedEventHandler(this.OnSysColorChange);
        }
 
        protected override void OnHandleDestroyed(EventArgs e) {
            SystemEvents.UserPreferenceChanged -= new UserPreferenceChangedEventHandler(this.OnSysColorChange);
            // We can leak this if we aren't disposed.
            //
            if (toolTip != null && !RecreatingHandle) {
                toolTip.Dispose();
                toolTip = null;
            }
            base.OnHandleDestroyed(e);
        }
 
        /*
        public bool OnHelp() {
            GridEntry gridEntry = GetGridEntryFromRow(selectedRow);
            if (gridEntry == null || !(Focused || Edit.Focused)) {
                return false;
            }
 
            string keyword = gridEntry.HelpKeyword;
            if (keyword != null && keyword.Length != 0) {
                try {
                    IHelpService hsvc = GetHelpService();
                    if (hsvc != null) {
                        hsvc.ShowHelpFromKeyword(keyword);
                    }
                }
                catch (Exception) {
                }
            }
            return true;
        }
        
        // This has no effect, see VSW#470693.
        protected override void OnImeModeChanged(EventArgs e) {
            // VSW  #375530
            // Only update edit box mode if actually out of sync with grid's mode (to avoid re-entrancy issues)
            //
            if (edit != null && edit.ImeMode != this.ImeMode) {
                // URT  #51190
                // Keep the ImeMode of the property grid and edit box in step
                //
                edit.ImeMode = this.ImeMode;
            }
            base.OnImeModeChanged(e);
        }                                       
       */
 
        private void OnListChange(object sender, EventArgs e) {
            Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose,  "PropertyGridView:OnListChange");
            if (!DropDownListBox.InSetSelectedIndex()) {
                GridEntry gridEntry = GetGridEntryFromRow(selectedRow);
                Edit.Text = gridEntry.GetPropertyTextValue(DropDownListBox.SelectedItem);
                Edit.FocusInternal();
                SelectEdit(false);
            }
            SetFlag(FlagDropDownCommit, true);
        }
 
        private void OnListMouseUp(object sender, MouseEventArgs me) {
            OnListClick(sender, me);
        }
 
        private void OnListClick(object sender, EventArgs e) {
            Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose,  "PropertyGridView:OnListClick");
            GridEntry gridEntry = GetGridEntryFromRow(selectedRow);
            
            if (DropDownListBox.Items.Count == 0) {
               CommonEditorHide();
               SetCommitError(ERROR_NONE);
               SelectRow(selectedRow);
               return;
            }
            else {
                object value = DropDownListBox.SelectedItem;
 
                // don't need the commit becuase we're committing anyway.
                //
                SetFlag(FlagDropDownCommit, false);
                if (value != null && !CommitText((string)value)) {
                    SetCommitError(ERROR_NONE);
                    SelectRow(selectedRow);
                }
            }
        }
        
        private void OnListDrawItem(object sender, DrawItemEventArgs die) {
            int index = die.Index;
 
            if (index < 0 || selectedGridEntry == null) {
                return;
            }
 
            string text = (string)DropDownListBox.Items[die.Index];
 
            Debug.WriteLineIf(GridViewDebugPaint.TraceVerbose,  "Drawing list item, value='" + text + "'");
            die.DrawBackground();
            die.DrawFocusRectangle();
 
            Rectangle drawBounds = die.Bounds;
            drawBounds.Y += 1;
            drawBounds.X -= 1;
            
 
            GridEntry gridEntry = GetGridEntryFromRow(selectedRow);
            try {
                DrawValue(die.Graphics, drawBounds, drawBounds, gridEntry, gridEntry.ConvertTextToValue(text), (int)(die.State & DrawItemState.Selected) != 0, false, false, false);
            }
            catch (FormatException ex) {
                ShowFormatExceptionMessage(gridEntry.PropertyLabel, text, ex);
                if (DropDownListBox.IsHandleCreated)
                    DropDownListBox.Visible = false;
            }
        }
 
        private void OnListKeyDown(object sender, KeyEventArgs ke) {
            if (ke.KeyCode == Keys.Return) {
                OnListClick(null, null);
                if (selectedGridEntry != null) {
                    selectedGridEntry.OnValueReturnKey();
                }
            }
 
            OnKeyDown(sender,ke);
        }
 
        protected override void OnLostFocus(EventArgs e) {
            Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose,  "PropertyGridView:OnLostFocus");
            Debug.WriteLineIf(GridViewDebugPaint.TraceVerbose,  "PropertyGridView lost focus");
            
            if (e != null) {
                base.OnLostFocus(e);
            }
            if (this.FocusInside) {
                base.OnLostFocus(e);
                return;
            }
            GridEntry gridEntry = GetGridEntryFromRow(selectedRow);
            if (gridEntry != null) {
                Debug.WriteLineIf(GridViewDebugPaint.TraceVerbose,  "removing gridEntry focus");
                gridEntry.Focus = false;;
                CommonEditorHide();
                InvalidateRow(selectedRow);
            }
            base.OnLostFocus(e);
 
            // For empty GridView, clear the focus indicator that was painted in OnGotFocus()
            if (totalProps <= 0 && AccessibilityImprovements.Level1) {
                using (Graphics g = CreateGraphicsInternal()) {
                    Rectangle clearRect = new Rectangle(1, 1, Size.Width - 2, Size.Height - 2);
                    Debug.WriteLineIf(GridViewDebugPaint.TraceVerbose, "Filling empty gridview rect=" + clearRect.ToString());
 
                    g.FillRectangle(backgroundBrush, clearRect);
                }
            }
        }
 
        private void OnEditChange(object sender, EventArgs e) {
            Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose,  "PropertyGridView:OnEditChange");
            SetCommitError(ERROR_NONE, Edit.Focused);
            
            ToolTip.ToolTip = "";
            ToolTip.Visible = false;
            
            if (!Edit.InSetText()) {
                GridEntry gridEntry = GetGridEntryFromRow(selectedRow);
                if (gridEntry != null && (gridEntry.Flags & GridEntry.FLAG_IMMEDIATELY_EDITABLE) != 0)
                    Commit();
            }
        }
 
        private void OnEditGotFocus(object sender, EventArgs e) {
            Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose,  "PropertyGridView:OnEditGotFocus");
 
            if (!Edit.Visible) {
                this.FocusInternal();
                return;
            }
 
            switch (errorState) {
                case ERROR_MSGBOX_UP:
                    return;
                case ERROR_THROWN:
                    if (Edit.Visible) {
                        Edit.HookMouseDown = true;
                    }
                    break;
                default:
                    if (this.NeedsCommit) {
                        SetCommitError(ERROR_NONE, true);
                    }
                    break;
            }
 
            if (selectedGridEntry != null && GetRowFromGridEntry(selectedGridEntry) != -1) {
                Debug.WriteLineIf(GridViewDebugPaint.TraceVerbose,  "adding gridEntry focus");
                selectedGridEntry.Focus = true;
                InvalidateRow(selectedRow);
                (Edit.AccessibilityObject as ControlAccessibleObject).NotifyClients(AccessibleEvents.Focus);
 
                if (AccessibilityImprovements.Level3) {
                    Edit.AccessibilityObject.SetFocus();
                }
            }
            else {
                SelectRow(0);
            }
        }
        /*
                private void OnEditImeModeChanged(object sender, EventArgs e) {
                    // URT  #51190
                    // The property grid ImeMode tracks the ImeMode of the edit control.
                    // We require this because the first character the goes into the edit control
                    // is composed while the PropertyGrid still has focus - so the ImeMode
                    // of the grid and the edit need to be the same or we get inconsistent IME composition.
                    //
                    if (this.ImeMode != edit.ImeMode) {
                        this.ImeMode = edit.ImeMode;
                    }
                }
        */
 
        private bool ProcessEnumUpAndDown(GridEntry gridEntry, Keys keyCode, bool closeDropDown = true) {
            object value = gridEntry.PropertyValue;
            object[] rgvalues = gridEntry.GetPropertyValueList();
            if (rgvalues != null) {
                for (int i = 0; i < rgvalues.Length; i++) {
                    object rgvalue = rgvalues[i];
                    if (value != null && rgvalue != null && value.GetType() != rgvalue.GetType() && gridEntry.TypeConverter.CanConvertTo(gridEntry, value.GetType())) {
                        rgvalue = gridEntry.TypeConverter.ConvertTo(gridEntry, CultureInfo.CurrentCulture, rgvalue, value.GetType());
                    }
 
                    bool equal = (value == rgvalue) || (value != null && value.Equals(rgvalue));
 
                    if (!equal && value is string && rgvalue != null) {
                        equal = 0 == String.Compare((string)value, rgvalue.ToString(), true, CultureInfo.CurrentCulture);
                    }
                    if (equal) {
                        object valueNew = null;
                        if (keyCode == Keys.Up) {
                            if (i == 0) return true;
                            valueNew = rgvalues[i - 1];
                        }
                        else {
                            if (i == rgvalues.Length - 1) return true;
                            valueNew = rgvalues[i + 1];
                        }
 
                        CommitValue(gridEntry, valueNew, closeDropDown);
                        SelectEdit(false);
                        return true;
                    }
                }
            }
 
            return false;
        }
 
        private void OnEditKeyDown(object sender, KeyEventArgs ke) {
            Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose,  "PropertyGridView:OnEditKeyDown");
            bool fAlt = ke.Alt;
            if (!fAlt && (ke.KeyCode == Keys.Up || ke.KeyCode == Keys.Down)) {
                GridEntry gridEntry = GetGridEntryFromRow(selectedRow);
                if (!gridEntry.Enumerable || !gridEntry.IsValueEditable) {
                    return;
                }
 
                ke.Handled = true;
                bool processed = ProcessEnumUpAndDown(gridEntry, ke.KeyCode);
                if (processed) {
                    return;
                }
            }
            // VS7 # 13336:  handle non-expand/collapse case of left & right as up & down
            else if ((ke.KeyCode == Keys.Left || ke.KeyCode == Keys.Right) &&
                     (ke.Modifiers & ~Keys.Shift) != 0) {
                return;
            }
            OnKeyDown(sender,ke);
        }
 
        private void OnEditKeyPress(object sender, KeyPressEventArgs ke) {
            Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose,  "PropertyGridView:OnEditKeyPress");
            GridEntry gridEntry = GetGridEntryFromRow(selectedRow);
            if (gridEntry == null)
                return;
 
            if (!gridEntry.IsTextEditable) {
                ke.Handled = FilterReadOnlyEditKeyPress(ke.KeyChar);
            }
        }
 
 
        private void OnEditLostFocus(object sender, EventArgs e) {
 
            Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose,  "PropertyGridView:OnEditLostFocus");
 
            // believe it or not, this can actually happen.
            if (Edit.Focused || (errorState == ERROR_MSGBOX_UP) || (errorState == ERROR_THROWN)|| GetInPropertySet()) {
                return;
            }
 
            // check to see if the focus is on the drop down or one of it's children
            // if so, return;
            if (dropDownHolder != null && dropDownHolder.Visible) {
                bool found = false;
                for (IntPtr hwnd = UnsafeNativeMethods.GetForegroundWindow();
                    hwnd != IntPtr.Zero; hwnd = UnsafeNativeMethods.GetParent(new HandleRef(null, hwnd))) {
                    if (hwnd == dropDownHolder.Handle) {
                        found = true;
                    }
                }
                if (found)
                    return;
            }
            
            if (this.FocusInside) {
               return;
            }
            
            // if the focus isn't goint to a child of the view
            if (!Commit()) {
                Edit.FocusInternal();
                return;
            }
            // change our focus state.
            this.InvokeLostFocus(this, EventArgs.Empty);
        }
 
        private void OnEditMouseDown(object sender, MouseEventArgs me) {
            Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose,  "PropertyGridView:OnEditMouseDown");
            
            if (!FocusInside) {
               SelectGridEntry(selectedGridEntry, false);
            }
            
            if (me.Clicks % 2 == 0) {
                DoubleClickRow(selectedRow,false, ROWVALUE);
                Edit.SelectAll();
            }
            
            if (rowSelectTime == 0) {
                return;
            }
            
            // check if the click happened within the double click time since the row was selected.
            // this allows the edits to be selected with two clicks instead of 3 (select row, double click).
            //
            long timeStamp = DateTime.Now.Ticks;
            int delta = (int)((timeStamp - rowSelectTime) / 10000); // make it milleseconds
            
            if (delta < SystemInformation.DoubleClickTime) {
 
                Point screenPoint = Edit.PointToScreen(new Point(me.X, me.Y));
 
                if (Math.Abs(screenPoint.X - rowSelectPos.X) < SystemInformation.DoubleClickSize.Width &&
                    Math.Abs(screenPoint.Y - rowSelectPos.Y) < SystemInformation.DoubleClickSize.Height) {
                    DoubleClickRow(selectedRow,false, ROWVALUE);
                    Edit.SendMessage(NativeMethods.WM_LBUTTONUP, 0, (int)(me.Y << 16 | (me.X & 0xFFFF)));
                    Edit.SelectAll();                
                }
                rowSelectPos = Point.Empty;
                
                rowSelectTime = 0;
            }
        }
      
        private bool OnF4(Control sender) {
            Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose,  "PropertyGridView:OnF4");
            if (ModifierKeys != 0) {
                return false;
            }
            if (sender == this || sender == this.ownerGrid)
                F4Selection(true);
            else
                UnfocusSelection();
            return true;
        }
 
        private bool OnEscape(Control sender) {
            Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose,  "PropertyGridView:OnEscape");
            if ((ModifierKeys & (Keys.Alt | Keys.Control)) != 0) {
                return false;
            }
 
            SetFlag(FlagDropDownCommit, false);
 
            if (sender == Edit && Edit.Focused) {
            
                // if we aren't in an error state, just quit
                if (errorState == ERROR_NONE) {
                   Edit.Text = originalTextValue;
                   FocusInternal();
                   return true;
                }
            
                if (this.NeedsCommit) {
                    bool success = false;
                    Edit.Text = originalTextValue;
                    bool needReset = true;
 
                    if (selectedGridEntry != null) {
                                    
                        string curTextValue = selectedGridEntry.GetPropertyTextValue();
                        needReset = originalTextValue != curTextValue && !(string.IsNullOrEmpty(originalTextValue) && string.IsNullOrEmpty(curTextValue));
                    }
        
                    if (needReset) {
                        try {
                            success = CommitText(originalTextValue);
                        }
                        catch {
                        }
                    }
                    else {
                        success = true;
                    }
 
                    // this would be an odd thing to happen, but...
                    if (!success) {
                        Edit.FocusInternal();
                        SelectEdit(false);
                        return true;
                    }
                }
 
                SetCommitError(ERROR_NONE);
                FocusInternal();
                return true;
            }
            else if (sender != this) {
                CloseDropDown();
                FocusInternal();
            }
            return false;
        }
 
        protected override void OnKeyDown(KeyEventArgs ke) {
            OnKeyDown(this,ke);
        }
 
        [
            SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters") // We want to commit empty text.
                                                                                                        // So we don't have to localize it.
        ]
        private void OnKeyDown(object sender, KeyEventArgs ke) {
 
            GridEntry gridEntry = GetGridEntryFromRow(selectedRow);
            if (gridEntry == null) return;
 
            ke.Handled = true;
            bool fControl = ke.Control;
            bool fShift = ke.Shift;
            bool fBoth = fControl && fShift;
            bool fAlt = ke.Alt;
            Keys keyCode = ke.KeyCode;
            bool fallingThorugh = false;
 
            // Microsoft, we have to do this here because if we are
            // hosted in a non-windows forms dialog, we never get a chance to
            // peek at the messages, we just get called,
            // so we have to do this here...
            //
            if (keyCode == Keys.Tab) {
                if (ProcessDialogKey(ke.KeyData)) {
                    ke.Handled = true;
                    return;
                }
            }
 
            // Alt-Arrow support... sigh...
            if (keyCode == Keys.Down && fAlt && DropDownButton.Visible) {
                F4Selection(false);
                return;
            }
 
            if (keyCode == Keys.Up && fAlt && DropDownButton.Visible && (dropDownHolder != null) && dropDownHolder.Visible) {
                UnfocusSelection();
                return;
            }
 
            if (ToolTip.Visible) {
                ToolTip.ToolTip = "";
            }
 
            if (fBoth || sender == this || sender == this.ownerGrid) {
                switch (keyCode) {
                    case Keys.Up:
                    case Keys.Down:
                        int pos = (keyCode == Keys.Up ? selectedRow - 1 : selectedRow + 1);
                        SelectGridEntry(GetGridEntryFromRow(pos),true);
                        SetFlag(FlagNoDefault, false);
                        return;
                    case Keys.Left:
                        if (fControl) {
                            // move the splitter 3 pixels to the left
                            MoveSplitterTo(InternalLabelWidth - 3);
                            return;
                        }
                        if (gridEntry.InternalExpanded)
                            SetExpand(gridEntry,false);
                        else {
                            // VS7 # 13336:  handle non-expand/collapse case of left & right as up & down
                            SelectGridEntry( GetGridEntryFromRow( selectedRow - 1 ), true );
                        }
                        return;
                    case Keys.Right:
                        if (fControl) {
                            // move the splitter 3 pixels to the right
                            MoveSplitterTo(InternalLabelWidth + 3);
                            return;
                        }
                        if (gridEntry.Expandable) {
                            if (gridEntry.InternalExpanded) {
                                GridEntryCollection rgipes2 = gridEntry.Children;
                                SelectGridEntry(rgipes2.GetEntry(0),true);
                            }
                            else
                                SetExpand(gridEntry,true);
                        }
                        else {
                            // VS7 # 13336:  handle non-expand/collapse case of left & right as up & down
                            SelectGridEntry( GetGridEntryFromRow( selectedRow + 1 ), true );
                        }
                        return;
                    case Keys.Return:
                        if (gridEntry.Expandable) {
                            SetExpand(gridEntry,!gridEntry.InternalExpanded);
                        }
                        else {
                           gridEntry.OnValueReturnKey();
                        }
                        
                        return;
                    case Keys.Home:
                    case Keys.End:
                        GridEntryCollection rgipes = GetAllGridEntries();
                        int pos2 = (keyCode == Keys.Home ? 0 : rgipes.Count-1);
                        SelectGridEntry(rgipes.GetEntry(pos2),true);
                        return;
                    case Keys.Add:
                    case Keys.Oemplus:
                    case Keys.OemMinus:
                    case Keys.Subtract:
 
                        if (!gridEntry.Expandable) {
                            break;
                        }
                        
                        SetFlag(FlagIsSpecialKey, true);
                        bool expand = (keyCode == Keys.Add || keyCode == Keys.Oemplus);
                        SetExpand(gridEntry,expand);
                        Invalidate();
                        ke.Handled = true;
                        return;
 
                
                case Keys.D8:
                        if (fShift) {
                            goto case Keys.Multiply;
                        }
                        break;
                case Keys.Multiply:
                        SetFlag(FlagIsSpecialKey, true);
                        RecursivelyExpand(gridEntry,true, true, MaxRecurseExpand);
                        ke.Handled = false;
                        return;
 
                    
                    
                    case Keys.Prior:  //PAGE_UP:
                    case Keys.Next: //PAGE_DOWN
                        
                        bool next = (keyCode == Keys.Next);
                        //int rowGoal = next ? visibleRows - 1 : 0;
                        int offset = next ? visibleRows - 1 : 1 - visibleRows;
 
                        int row = selectedRow;
 
                        if (fControl && !fShift) {
                            return;
                        }
                        if (selectedRow != -1) { // actual paging.
 
                            int start = GetScrollOffset();
                            SetScrollOffset(start + offset);
                            SetConstants();
                            if (GetScrollOffset() != (start + offset)) {
                                // we didn't make a full page
                                if (next) {
                                    row = visibleRows - 1;
                                }
                                else {
                                    row = 0;
                                }
                            }
                        }
 
                        SelectRow(row);
                        Refresh();
                        return;   
                    
                    // Copy/paste support...    
                    
                    case Keys.Insert:
                        if (fShift && !fControl && !fAlt) {
                           fallingThorugh = true;
                           goto case Keys.V;
                        }
                        goto case Keys.C;
                    case Keys.C:
                        // copy text in current property
                        if (fControl && !fAlt && !fShift) {
                           DoCopyCommand();
                           return;
                        }
                        break;
                    case Keys.Delete:
                        // cut text in current property
                        if (fShift && !fControl && !fAlt) {
                           fallingThorugh = true;
                           goto case Keys.X;
                        }
                        break;
                    case Keys.X:
                        // cut text in current property
                        if (fallingThorugh || (fControl && !fAlt && !fShift)) {
                           Clipboard.SetDataObject(gridEntry.GetPropertyTextValue());
                           CommitText("");
                           return;
                        }
                        break;
                    case Keys.V:
                        // paste the text
                        if (fallingThorugh || (fControl && !fAlt && !fShift)) {
                           DoPasteCommand();
                        }
                        break;
                    case Keys.A:
                        if (fControl && !fAlt && !fShift && Edit.Visible) {
                           Edit.FocusInternal();
                           Edit.SelectAll();       
                        }
                        break;
                }
            }
 
            if (gridEntry != null && ke.KeyData == (Keys.C | Keys.Alt | Keys.Shift | Keys.Control)) {
                Clipboard.SetDataObject(gridEntry.GetTestingInfo());
                return;
            }
 
            /* Microsoft, VS30371 -- Due to conflicts with other VS commands,
               we are removing this functionality.
 
               // Ctrl + Shift + 'X' selects the property that starts with 'X'
               if (fBoth) {
                   // now get the array to work with.
                   GridEntry[] rgipes = GetAllGridEntries();
                   int cLength = rgipes.Length;
 
                   // now get our char.
                   string strCh = (new string(new char[] {(char)ke.KeyCode})).ToLower(CultureInfo.InvariantCulture);
 
                   int cCur = -1;
                   if (gridEntry != null)
                       for (int i = 0; i < cLength; i++) {
                           if (rgipes[i] == gridEntry) {
                               cCur = i;
                               break;
                           }
                       }
 
                   cCur += 1; // this indicated where we start...
                   // find next label that starts with this letter.
                   for (int i = 0; i < cLength; i++) {
                       GridEntry ipeCur = rgipes[(i + cCur) % cLength];
                       if (ipeCur.PropertyLabel.ToLower(CultureInfo.InvariantCulture).StartsWith(strCh)) {
                           if (gridEntry != ipeCur) {
                               SelectGridEntry(ipeCur,true);
                               return;
                           }
                           break;
                       }
                   }
               }
            */
 
            if (AccessibilityImprovements.Level3 && selectedGridEntry.Enumerable &&
                dropDownHolder != null && dropDownHolder.Visible &&
                (keyCode == Keys.Up || keyCode == Keys.Down))
            {
                ProcessEnumUpAndDown(selectedGridEntry, keyCode, false);
            }
 
            ke.Handled = false;
            return;
        }
 
        protected override void OnKeyPress(KeyPressEventArgs ke) {
 
            bool fControl = false; //ke.getControl();
            bool fShift = false; //ke.getShift();
            bool fBoth = fControl && fShift;
            if (!fBoth && WillFilterKeyPress(ke.KeyChar))
                // find next property with letter typed.
                FilterKeyPress(ke.KeyChar);
            SetFlag(FlagIsSpecialKey, false);
        }
 
        protected override void OnMouseDown(MouseEventArgs me) {
            // check for a splitter
            if (me.Button == MouseButtons.Left && SplitterInside(me.X,me.Y) && totalProps != 0) {
                if (!Commit()) {
                    return;
                }
                
                if (me.Clicks == 2) {
                     MoveSplitterTo(this.Width / 2);
                     return;
                }
                
                UnfocusSelection();
                SetFlag(FlagIsSplitterMove, true);
                tipInfo = -1;
                CaptureInternal = true;
                return;
            }
 
            // are ew on a propentry?
            Point pos = FindPosition(me.X,me.Y);
 
            if (pos == InvalidPosition) {
                return;
            }
 
            // Notify that prop entry of the click...but normalize
            // it's coords first...we really  just need the x, y
            GridEntry gridEntry = GetGridEntryFromRow(pos.Y);
 
            if (gridEntry != null) {
                // get the origin of this pe
                Rectangle r = GetRectangle(pos.Y, ROWLABEL);
                
                lastMouseDown = new Point(me.X, me.Y);
 
                // offset the mouse points
                // notify the prop entry
                if (me.Button == MouseButtons.Left) {
                    gridEntry.OnMouseClick(me.X - r.X, me.Y - r.Y, me.Clicks, me.Button);
                }
                else {
                    SelectGridEntry(gridEntry, false);
                }
                lastMouseDown = InvalidPosition;
                gridEntry.Focus = true;
                SetFlag(FlagNoDefault, false);
            }
        }
 
        // this will make tool tip go away.
        protected override void OnMouseLeave(EventArgs e) {
            if (!GetFlag(FlagIsSplitterMove))
                Cursor = Cursors.Default; // Cursor = null;;
 
            base.OnMouseLeave(e);
        }
 
        protected override void OnMouseMove(MouseEventArgs me) {
            int rowMoveCur;
            Point pt = Point.Empty;
            bool onLabel = false;
 
            if (me == null) {
                rowMoveCur = -1;
                pt = InvalidPosition;
            }
            else {
                pt = FindPosition(me.X,me.Y);
                if (pt == InvalidPosition || (pt.X != ROWLABEL && pt.X != ROWVALUE)) {
                    rowMoveCur = -1;
                    ToolTip.ToolTip = "";
                }
                else {
                    rowMoveCur = pt.Y;
                    onLabel = pt.X == ROWLABEL;
                }
 
            }
 
            if (pt == InvalidPosition || me == null) {
                return;
            }
 
            if (GetFlag(FlagIsSplitterMove)) {
                MoveSplitterTo(me.X);
            }
            
            if ((rowMoveCur != this.TipRow || pt.X != this.TipColumn)  && !GetFlag(FlagIsSplitterMove)) {
                GridEntry gridItem = GetGridEntryFromRow(rowMoveCur);
                string tip = "";
                tipInfo = -1;
 
                if (gridItem != null) {
                    Rectangle itemRect = GetRectangle(pt.Y, pt.X);
                    if (onLabel && gridItem.GetLabelToolTipLocation(me.X - itemRect.X, me.Y - itemRect.Y) != InvalidPoint) {
                        tip = gridItem.LabelToolTipText;
                        this.TipRow = rowMoveCur;
                        this.TipColumn = pt.X;
                    }
                    else if (!onLabel && gridItem.ValueToolTipLocation != InvalidPoint && !Edit.Focused) {
                        if (!this.NeedsCommit) {
                           tip = gridItem.GetPropertyTextValue();
                        }
                        this.TipRow = rowMoveCur;
                        this.TipColumn = pt.X;
                    }
                }
 
                // VSWhidbey 94890: Ensure that tooltips don't display when host application is not foreground app.
                // Assume that we don't want to display the tooltips
                IntPtr  foregroundWindow = UnsafeNativeMethods.GetForegroundWindow();
                if (UnsafeNativeMethods.IsChild(new HandleRef(null, foregroundWindow), new HandleRef(null, this.Handle))) {
                    // vs  75848 -- don't show the tips if a
                    // dropdown is showing
                    if ((dropDownHolder == null || dropDownHolder.Component == null) || rowMoveCur == selectedRow) {
                        ToolTip.ToolTip = tip;
                    }
                }
                else {
                    ToolTip.ToolTip = "";
                }
            }
 
            if (totalProps != 0 && (SplitterInside(me.X,me.Y) || GetFlag(FlagIsSplitterMove))) {
                Cursor = Cursors.VSplit;
            }
            else {
                Cursor = Cursors.Default; // Cursor = null;;
            }
            base.OnMouseMove(me);
        }
 
        protected override void OnMouseUp(MouseEventArgs me) {
            CancelSplitterMove();
        }
 
        protected override void OnMouseWheel(MouseEventArgs me) {
        
            this.ownerGrid.OnGridViewMouseWheel(me);
 
            HandledMouseEventArgs e = me as HandledMouseEventArgs;
            if (e != null) {
               if (e.Handled) {
                   return;
               }
               e.Handled = true;
            }
            
            if ((ModifierKeys & (Keys.Shift | Keys.Alt)) != 0 || MouseButtons != MouseButtons.None) {
                return; // Do not scroll when Shift or Alt key is down, or when a mouse button is down.
            }
 
            int wheelScrollLines = SystemInformation.MouseWheelScrollLines;
            if (wheelScrollLines == 0) {
                return; // Do not scroll when the user system setting is 0 lines per notch
            }
 
            Debug.Assert(this.cumulativeVerticalWheelDelta > -NativeMethods.WHEEL_DELTA, "cumulativeVerticalWheelDelta is too small");
            Debug.Assert(this.cumulativeVerticalWheelDelta < NativeMethods.WHEEL_DELTA, "cumulativeVerticalWheelDelta is too big");
 
            // Should this only work if the Edit has focus?  anyway
            // we use the mouse wheel to change the values in the dropdown if it's
            // an enumerable value.
            //
            if (selectedGridEntry != null && selectedGridEntry.Enumerable && Edit.Focused && selectedGridEntry.IsValueEditable) {
                  int index = GetCurrentValueIndex(selectedGridEntry);
                  if (index != -1) {
                     int delta = me.Delta > 0 ? -1 : 1;
                     object[] values = selectedGridEntry.GetPropertyValueList();
 
                     if (delta > 0 && index >= (values.Length - 1)) {
                        index = 0;
                     }
                     else if (delta < 0 && index == 0) {
                        index = values.Length - 1;
                     }
                     else {
                        index += delta;
                     }
 
                     CommitValue(values[index]);
                     SelectGridEntry(selectedGridEntry, true);
                     Edit.FocusInternal();
                     return;
                  }
            }
 
 
            int initialOffset = GetScrollOffset();
            cumulativeVerticalWheelDelta += me.Delta;
            float partialNotches = (float)cumulativeVerticalWheelDelta / (float)NativeMethods.WHEEL_DELTA;
            int fullNotches = (int) partialNotches;
 
            if (wheelScrollLines == -1) {
 
               // Equivalent to large change scrolls
               if (fullNotches != 0) {
   
                    int originalOffset = initialOffset;
            int large = fullNotches * this.scrollBar.LargeChange;
                    int newOffset = Math.Max(0,initialOffset - large);
                    newOffset = Math.Min(newOffset, totalProps - visibleRows+1);
 
            
                    initialOffset -= fullNotches * this.scrollBar.LargeChange;
                    if (Math.Abs(initialOffset - originalOffset) >= Math.Abs(fullNotches * this.scrollBar.LargeChange)) {
                        this.cumulativeVerticalWheelDelta -= fullNotches * NativeMethods.WHEEL_DELTA;
                    }
                    else {
                        this.cumulativeVerticalWheelDelta = 0;
                    }
                    
                    if (!ScrollRows(newOffset)) {
            this.cumulativeVerticalWheelDelta = 0;
                        return;
                    }
                }
            }
            else {   
         
        
                 // SystemInformation.MouseWheelScrollLines doesn't work under terminal server, 
                 // it default to the notches per scroll.
                 int scrollBands = (int) ((float) wheelScrollLines * partialNotches);
                   
                 if (scrollBands != 0) {
                    
                    if (ToolTip.Visible) {
                        ToolTip.ToolTip = "";
                    }
 
                    int newOffset = Math.Max(0,initialOffset - scrollBands);
                    newOffset = Math.Min(newOffset, totalProps - visibleRows+1);
 
                    if (scrollBands > 0) {
 
                        if (this.scrollBar.Value <= this.scrollBar.Minimum) {
                            this.cumulativeVerticalWheelDelta = 0;
                        }
                        else {
                            this.cumulativeVerticalWheelDelta -= (int)((float)scrollBands  * ((float)NativeMethods.WHEEL_DELTA / (float)wheelScrollLines));
                        }
                    }
                    else {
 
                        if (this.scrollBar.Value > (scrollBar.Maximum-visibleRows+1)) {
                            this.cumulativeVerticalWheelDelta = 0;
                        }
                        else {
                            this.cumulativeVerticalWheelDelta -= (int)((float)scrollBands * ((float)NativeMethods.WHEEL_DELTA / (float)wheelScrollLines));
                        }
                    }
 
            if (!ScrollRows(newOffset)) {
            this.cumulativeVerticalWheelDelta = 0;
            return;
            }
                }
                else {
                    this.cumulativeVerticalWheelDelta = 0;
                }
 
            }
        }
 
        protected override void OnMove(EventArgs e) {
            CloseDropDown();
        }
        
        protected override void OnPaintBackground(PaintEventArgs pe) {
        }
 
        protected override void OnPaint(PaintEventArgs pe) {
            Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose,  "PropertyGridView:OnPaint");
            Debug.WriteLineIf(GridViewDebugPaint.TraceVerbose,  "On paint called.  Rect=" + pe.ClipRectangle.ToString());
            Graphics g = pe.Graphics;
 
            int yPos = 0;
            int startRow = 0;
            int endRow = visibleRows - 1;
            
            Rectangle clipRect = pe.ClipRectangle;
            
            // give ourselves a little breathing room to account for lines, etc., as well
            // as the entries themselves.
            //
            clipRect.Inflate(0,2);
 
            try {
                Size sizeWindow = this.Size;
 
                // figure out what rows we're painting
                Point posStart = FindPosition(clipRect.X, clipRect.Y);
                Point posEnd = FindPosition(clipRect.X, clipRect.Y + clipRect.Height);
                if (posStart != InvalidPosition) {
                    startRow = Math.Max(0,posStart.Y);
                }
 
                if (posEnd != InvalidPosition) {
                    endRow   = posEnd.Y;
                }
 
                int cPropsVisible = Math.Min(totalProps - GetScrollOffset(),1+visibleRows);
                
#if DEBUG
                GridEntry debugIPEStart = GetGridEntryFromRow(startRow);
                GridEntry debugIPEEnd   = GetGridEntryFromRow(endRow);
                string startName = debugIPEStart == null ? null : debugIPEStart.PropertyLabel;
                if (startName == null) {
                    startName = "(null)";
                }
                string endName = debugIPEEnd == null ? null : debugIPEEnd.PropertyLabel;
                if (endName == null) {
                    endName = "(null)";
                }
#endif
 
                SetFlag(FlagNeedsRefresh, false);
 
                //SetConstants();
 
                Size size = this.GetOurSize();
                Point loc = this.ptOurLocation;
 
                if (GetGridEntryFromRow(cPropsVisible-1) == null) {
                    cPropsVisible--;
                }
 
 
                // if we actually have some properties, then start drawing the grid
                //
                if (totalProps > 0) {
                
                    // draw splitter
                    cPropsVisible = Math.Min(cPropsVisible, endRow+1);
 
                    Debug.WriteLineIf(GridViewDebugPaint.TraceVerbose,  "Drawing splitter");
                    Pen splitterPen = new Pen(ownerGrid.LineColor, GetSplitterWidth());
                    splitterPen.DashStyle = DashStyle.Solid;
                    g.DrawLine(splitterPen, labelWidth,loc.Y,labelWidth, (cPropsVisible)*(RowHeight+1)+loc.Y);
                    splitterPen.Dispose();
 
                    // draw lines.
                    Debug.WriteLineIf(GridViewDebugPaint.TraceVerbose,  "Drawing lines");
                    Pen linePen = new Pen(g.GetNearestColor(ownerGrid.LineColor));
                    
                    int cHeightCurRow = 0;
                    int cLineEnd = loc.X + size.Width;
                    int cLineStart = loc.X;
 
                    // draw values.
                    int totalWidth = GetTotalWidth() + 1;
                    //g.TextColor = ownerGrid.TextColor;
 
                    // draw labels. set clip rect.
                    for (int i = startRow; i < cPropsVisible; i++) {
                        try {
 
                            // draw the line
                            cHeightCurRow = (i)*(RowHeight+1) + loc.Y;
                            g.DrawLine(linePen, cLineStart,cHeightCurRow,cLineEnd,cHeightCurRow);
 
                            // draw the value
                            DrawValueEntry(g,i, ref clipRect);
 
                            // draw the label
                            Rectangle rect = GetRectangle(i,ROWLABEL);
                            yPos = rect.Y + rect.Height;
                            DrawLabel(g,i, rect, (i==selectedRow),false, ref clipRect);
                            if (i == selectedRow) {
                                Edit.Invalidate();
                            }
 
                        }
                        catch {
                            Debug.WriteLineIf(GridViewDebugPaint.TraceVerbose,  "Exception thrown during painting property " + GetGridEntryFromRow(i).PropertyLabel);
                        }
                    }
 
                    // draw the bottom line
                    cHeightCurRow = (cPropsVisible)*(RowHeight+1) + loc.Y;
                    g.DrawLine(linePen, cLineStart,cHeightCurRow,cLineEnd,cHeightCurRow);
                    
                    linePen.Dispose();
                }
 
                // fill anything left with window
                if (yPos < Size.Height) {
                    yPos++;
                    Rectangle clearRect = new Rectangle(1, yPos, Size.Width - 2, Size.Height - yPos - 1);
                    Debug.WriteLineIf(GridViewDebugPaint.TraceVerbose,  "Filling remaining area rect=" + clearRect.ToString());
                    
                    g.FillRectangle(backgroundBrush, clearRect);
                }
 
                // draw outside border
                using (Pen borderPen = new Pen(ownerGrid.ViewBorderColor, 1)) {
                    g.DrawRectangle(borderPen, 0, 0, sizeWindow.Width - 1, sizeWindow.Height - 1);
                }
                
                fontBold = null;
            }
            catch {
                Debug.Fail("Caught exception in OnPaint");
                // Do nothing.
            }
            finally {
                ClearCachedFontInfo();
            }
        }
 
        private void OnGridEntryLabelDoubleClick(object s, EventArgs e) {
            GridEntry gridEntry = (GridEntry)s;
 
            // if we've changed since the click (probably because we moved a row into view), bail
            //
            if (gridEntry != lastClickedEntry) {
                return;
            }
            int row = GetRowFromGridEntry(gridEntry);
            DoubleClickRow(row, gridEntry.Expandable, ROWLABEL);
        }
 
        private void OnGridEntryValueDoubleClick(object s, EventArgs e) {
 
            GridEntry gridEntry = (GridEntry)s;
            // if we've changed since the click (probably because we moved a row into view), bail
            //
            if (gridEntry != lastClickedEntry) {
                return;
            } 
            int row = GetRowFromGridEntry(gridEntry);
            DoubleClickRow(row, gridEntry.Expandable, ROWVALUE);
        }
 
        private void OnGridEntryLabelClick(object s, EventArgs e) {
            this.lastClickedEntry = (GridEntry)s;
            SelectGridEntry(lastClickedEntry, true);
        }
 
        private void OnGridEntryOutlineClick(object s, EventArgs e) {
            GridEntry gridEntry = (GridEntry)s;
            Debug.Assert(gridEntry.Expandable, "non-expandable IPE firing outline click");
 
            Cursor oldCursor = Cursor;
            if (!ShouldSerializeCursor()) {
                oldCursor = null;
            }
            Cursor = Cursors.WaitCursor;
 
            try {
                SetExpand(gridEntry, !gridEntry.InternalExpanded);
                SelectGridEntry(gridEntry, false);
            }
            finally {
                Cursor = oldCursor;
            }
        }
 
        private void OnGridEntryValueClick(object s, EventArgs e) {
        
            this.lastClickedEntry = (GridEntry)s;
            bool setSelectTime = s != selectedGridEntry;
            SelectGridEntry(lastClickedEntry, true);
            Edit.FocusInternal();
            
            if (lastMouseDown != InvalidPosition) {
            
               // clear the row select time so we don't interpret this as a double click.
               //
               rowSelectTime = 0;    
               
               Point editPoint = PointToScreen(lastMouseDown);
               editPoint = Edit.PointToClientInternal(editPoint);
               Edit.SendMessage(NativeMethods.WM_LBUTTONDOWN, 0, (int)(editPoint.Y << 16 | (editPoint.X & 0xFFFF))); 
               Edit.SendMessage(NativeMethods.WM_LBUTTONUP, 0, (int)(editPoint.Y << 16 | (editPoint.X & 0xFFFF))); 
            }
            
            if (setSelectTime) {
               rowSelectTime = DateTime.Now.Ticks;
               rowSelectPos  = PointToScreen(lastMouseDown);
            }
            else {
               rowSelectTime = 0;
               rowSelectPos = Point.Empty;
            }
        }
 
        private void ClearCachedFontInfo() {
            if (baseHfont != IntPtr.Zero) {
                SafeNativeMethods.ExternalDeleteObject(new HandleRef(this, baseHfont));
                baseHfont = IntPtr.Zero;
            }
            if (boldHfont != IntPtr.Zero) {
                SafeNativeMethods.ExternalDeleteObject(new HandleRef(this, boldHfont));
                boldHfont = IntPtr.Zero;
            }
        }
 
        protected override void OnFontChanged(EventArgs e) {
        
            ClearCachedFontInfo();
            cachedRowHeight = -1;
 
            if (this.Disposing || this.ParentInternal == null || this.ParentInternal.Disposing) {
                return;
            }
         
            fontBold = null;    // URT  #45662 - fontBold is cached based on Font                        
                        
            ToolTip.Font = this.Font;
            SetFlag(FlagNeedUpdateUIBasedOnFont, true);
            UpdateUIBasedOnFont(true);
            base.OnFontChanged(e);
 
            if (selectedGridEntry != null) {
                SelectGridEntry(selectedGridEntry, true);
            }
        }
 
        protected override void OnVisibleChanged(EventArgs e) {
            if (this.Disposing || this.ParentInternal == null || this.ParentInternal.Disposing) {
                return;
            }
         
            if (this.Visible && this.ParentInternal != null) {
                 SetConstants();
                 if (selectedGridEntry != null) {
                       SelectGridEntry(selectedGridEntry, true);
                 }
                 if (toolTip != null) {
                     ToolTip.Font = this.Font;
                 }
                 
            }
 
            base.OnVisibleChanged(e);
        }
        
        // a GridEntry recreated its children
        protected virtual void OnRecreateChildren(object s, GridEntryRecreateChildrenEventArgs e) {
            GridEntry parent = (GridEntry) s;
 
            if (parent.Expanded) {
                                          
                GridEntry[] entries = new GridEntry[allGridEntries.Count];
                allGridEntries.CopyTo(entries, 0);
                
                // find the index of the gridEntry that fired the event in our main list.
                int parentIndex = -1;
                for (int i = 0; i < entries.Length; i++) {
                    if (entries[i] == parent) {
                        parentIndex = i;
                        break;
                    }
                }
                
                Debug.Assert(parentIndex != -1, "parent GridEntry not found in allGridEntries");
                
                // clear our existing handlers
                ClearGridEntryEvents(allGridEntries, parentIndex + 1, e.OldChildCount);
                
                // resize the array if it's changed
                if (e.OldChildCount != e.NewChildCount) {
                    int newArraySize = entries.Length + (e.NewChildCount - e.OldChildCount);
                    GridEntry[] newEntries = new GridEntry[newArraySize];
                    
                    // copy the existing entries up to the parent
                    Array.Copy(entries, 0, newEntries, 0, parentIndex + 1);
                    
                    // copy the entries after the spot we'll be putting the new ones
                    Array.Copy(entries, parentIndex + e.OldChildCount+1, newEntries, parentIndex + e.NewChildCount+1, entries.Length - (parentIndex + e.OldChildCount + 1));
                    
                    entries = newEntries;
                }
                
                // from that point, replace the children with tne new children.
                GridEntryCollection children = parent.Children;
                int childCount = children.Count;
                
                Debug.Assert(childCount == e.NewChildCount, "parent reports " + childCount + " new children, event reports " + e.NewChildCount);
                
                // replace the changed items
                for (int i = 0; i < childCount; i++) {
                    entries[parentIndex + i + 1] = children.GetEntry(i);
                }
                
                // reset the array, rehook the handlers.
                allGridEntries.Clear();
                allGridEntries.AddRange(entries);
                AddGridEntryEvents(allGridEntries, parentIndex + 1, childCount);            
                
            }
 
            if (e.OldChildCount != e.NewChildCount) {
                totalProps = CountPropsFromOutline(topLevelGridEntries);
                SetConstants();
            }
            Invalidate();
        }
        
        protected override void OnResize(EventArgs e) {
            Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose,  "PropertyGridView:OnResize");
 
            Rectangle newRect = ClientRectangle;
            int       yDelta = lastClientRect == Rectangle.Empty  ? 0 : newRect.Height - lastClientRect.Height;
            bool   lastRow = (selectedRow+1) == visibleRows;
 
            // if we are hiding or showing the scroll bar, update the selected row
            // or if we are changing widths
            //
            bool sbVisible = ScrollBar.Visible;
 
            if (!lastClientRect.IsEmpty && newRect.Width > lastClientRect.Width) {
                Rectangle rectInvalidate = new Rectangle(lastClientRect.Width-1,0,newRect.Width-lastClientRect.Width+1,lastClientRect.Height);
                Invalidate(rectInvalidate);
            }
 
            if (!lastClientRect.IsEmpty && yDelta > 0) {
                Rectangle rectInvalidate = new Rectangle(0,lastClientRect.Height-1,lastClientRect.Width,newRect.Height-lastClientRect.Height+1);
                Invalidate(rectInvalidate);
            }
 
            int scroll = GetScrollOffset();
            SetScrollOffset(0);
            SetConstants();
            SetScrollOffset(scroll);
 
            if (DpiHelper.EnableDpiChangedHighDpiImprovements) {
                SetFlag(FlagNeedUpdateUIBasedOnFont, true);
                UpdateUIBasedOnFont(true);
                base.OnFontChanged(e);
            }
 
            CommonEditorHide();
 
            LayoutWindow(false);
            // vs  69679
            bool selectionVisible = (selectedGridEntry != null && selectedRow >=0  && selectedRow <= visibleRows);
            SelectGridEntry(selectedGridEntry, selectionVisible);
            lastClientRect = newRect;
        }
 
        private void OnScroll(object sender, ScrollEventArgs se) {
            Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose,  "PropertyGridView:OnScroll(" + ScrollBar.Value.ToString(CultureInfo.InvariantCulture) + " -> " + se.NewValue.ToString(CultureInfo.InvariantCulture) +")");
 
            if (!Commit() || !IsScrollValueValid(se.NewValue)) {
                // cancel the move
                se.NewValue = ScrollBar.Value;
                return;
            }
 
            int oldRow = -1;
            GridEntry oldGridEntry = selectedGridEntry;
            if (selectedGridEntry != null) {
                oldRow = GetRowFromGridEntry(oldGridEntry);
                Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose,  "OnScroll: SelectedGridEntry=" + oldGridEntry.PropertyLabel);
            }
 
            ScrollBar.Value = se.NewValue;
            if (oldGridEntry != null) {
                // we need to zero out the selected row so we don't try to commit again...since selectedRow is now bogus.
                selectedRow = -1;
                SelectGridEntry(oldGridEntry, (ScrollBar.Value == totalProps ? true : false));
                int newRow = GetRowFromGridEntry(oldGridEntry);
                if (oldRow != newRow) {
                    Invalidate(); 
                }
            }
            else {
                Invalidate();
            }
        }
 
        private void OnSysColorChange(object sender, UserPreferenceChangedEventArgs e) {
            if (e.Category == UserPreferenceCategory.Color || e.Category == UserPreferenceCategory.Accessibility) {
                SetFlag(FlagNeedUpdateUIBasedOnFont, true);
            }
        }
        
        public virtual void PopupDialog(int row) {
            Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose,  "PropertyGridView:PopupDialog");
            GridEntry gridEntry = GetGridEntryFromRow(row);
            if (gridEntry != null) {
                if (dropDownHolder != null && dropDownHolder.GetUsed()) {
                    CloseDropDown();
                    return;
                }
 
                bool fBtnDropDown = gridEntry.NeedsDropDownButton;
                bool fEnum = gridEntry.Enumerable;
                bool fBtnDialog = gridEntry.NeedsCustomEditorButton;
                if (fEnum && !fBtnDropDown) {
                    DropDownListBox.Items.Clear();
                    object value = gridEntry.PropertyValue;
                    object[] rgItems = gridEntry.GetPropertyValueList();
                    int maxWidth = 0;
 
                    // The listbox draws with GDI, not GDI+.  So, we
                    // use a normal DC here.
                    //
                    IntPtr hdc = UnsafeNativeMethods.GetDC(new HandleRef(DropDownListBox, DropDownListBox.Handle));
                    IntPtr hFont = Font.ToHfont();
                    System.Internal.HandleCollector.Add(hFont, NativeMethods.CommonHandles.GDI);
                    NativeMethods.TEXTMETRIC tm = new NativeMethods.TEXTMETRIC();
                    int iSel = -1;
                    try {
                        hFont = SafeNativeMethods.SelectObject(new HandleRef(DropDownListBox, hdc), new HandleRef(Font, hFont));
                        
                        iSel = GetCurrentValueIndex(gridEntry);
                        if (rgItems != null && rgItems.Length > 0) {
                            string s;
                            IntNativeMethods.SIZE textSize = new IntNativeMethods.SIZE();
                            
                            for (int i = 0; i < rgItems.Length; i++) {
                                s = gridEntry.GetPropertyTextValue(rgItems[i]);
                                DropDownListBox.Items.Add(s);        
                                IntUnsafeNativeMethods.GetTextExtentPoint32(new HandleRef(DropDownListBox, hdc), s, textSize);
                                maxWidth = Math.Max((int) textSize.cx, maxWidth);
                            }
                        }
                        SafeNativeMethods.GetTextMetrics(new HandleRef(DropDownListBox, hdc), ref tm);
                        
                        // border + padding + scrollbar
                        maxWidth += 2 + tm.tmMaxCharWidth + SystemInformation.VerticalScrollBarWidth;
                        
                        hFont = SafeNativeMethods.SelectObject(new HandleRef(DropDownListBox, hdc), new HandleRef(Font, hFont));
                    }
                    finally {
                        SafeNativeMethods.DeleteObject(new HandleRef(Font, hFont));
                        UnsafeNativeMethods.ReleaseDC(new HandleRef(DropDownListBox, DropDownListBox.Handle), new HandleRef(DropDownListBox, hdc));
                    }
                    
                    // Microsoft, 4/25/1998 - must check for -1 and not call the set...
                    if (iSel != -1) {
                        DropDownListBox.SelectedIndex = iSel;
                    }
                    SetFlag(FlagDropDownCommit, false);
                    DropDownListBox.Height = Math.Max(tm.tmHeight + 2, Math.Min(maxListBoxHeight, DropDownListBox.PreferredHeight));
                    DropDownListBox.Width = Math.Max(maxWidth, GetRectangle(row,ROWVALUE).Width);
                    try {
                        bool resizable = DropDownListBox.Items.Count > (DropDownListBox.Height / DropDownListBox.ItemHeight);
                        SetFlag(FlagResizableDropDown, resizable);
                        DropDownControl(DropDownListBox);
                    }
                    finally {
                        SetFlag(FlagResizableDropDown, false);
                    }
                    
                    Refresh();
                }
                else if (fBtnDialog || fBtnDropDown) {
                     try {
                        SetFlag(FlagInPropertySet, true);
                        Edit.DisableMouseHook = true;
 
                        try {
                            SetFlag(FlagResizableDropDown, gridEntry.UITypeEditor.IsDropDownResizable);
                            gridEntry.EditPropertyValue(this);
                        }
                        finally {
                            SetFlag(FlagResizableDropDown, false);
                        }
                     }
                     finally {
                        SetFlag(FlagInPropertySet, false);
                        Edit.DisableMouseHook = false;
                     }
                     Refresh();
                     
                     // as/urt  31468 -- we can't do this because
                     // some dialogs are non-modal, and
                     // this will pull focus from them.
                     // See ASURT 31468.
                     //
                     //if (fBtnDialog) {
                     //      this.Focus();
                     //}
                     
                     if (FocusInside) {
                        SelectGridEntry(gridEntry, false);
                     }
                }
            }
        }
 
        protected override bool ProcessDialogKey(Keys keyData) {
            Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose,  "PropertyGridView:ProcessDialogKey");
            if (HasEntries) {
                Keys keyCode = keyData & Keys.KeyCode;
                switch (keyCode) {
                    case Keys.F4:
                        if (FocusInside) {
                            return OnF4(this);
                        }
                        break;
 
                    case Keys.Tab:
 
                        if (((keyData & Keys.Control) != 0) || 
                            ((keyData & Keys.Alt) != 0)) {
                            break;
                        }
                        
                        bool forward = (keyData & Keys.Shift) == 0;
 
                        Control focusedControl = Control.FromHandleInternal(UnsafeNativeMethods.GetFocus());
 
                        if (focusedControl == null || !IsMyChild(focusedControl)) {
                            if (forward) {
                                TabSelection();
                                focusedControl = Control.FromHandleInternal(UnsafeNativeMethods.GetFocus());
                                // make sure the value actually took the focus
                                if (IsMyChild(focusedControl)) {
                                    return true;
                                }
                                else {
                                    return base.ProcessDialogKey(keyData);
                                }
 
                            }
                            else {
                                break;
                            }
                        }
                        else {
                            // one of our editors has focus
 
                            if (Edit.Focused) {
                                if (forward) {
                                    if (DropDownButton.Visible) {
                                        DropDownButton.FocusInternal();
                                        return true;
                                    }
                                    else if (DialogButton.Visible) {
                                        DialogButton.FocusInternal();
                                        return true;
                                    }
                                    // fall through
                                }
                                else {
                                    SelectGridEntry(GetGridEntryFromRow(selectedRow), false);
                                    return true;
                                }
                            }
                            else if (DialogButton.Focused || DropDownButton.Focused) {
                                if (!forward && Edit.Visible) {
                                    Edit.FocusInternal();
                                    return true;
                                }
                                // fall through
                            }
                        }
                        break;  
                    case Keys.Up:
                    case Keys.Down:
                    case Keys.Left:
                    case Keys.Right:
                        return false;
                    case Keys.Return:
                        if (DialogButton.Focused || DropDownButton.Focused) {
                           OnBtnClick((DialogButton.Focused ? DialogButton : DropDownButton), new EventArgs());
                           return true;
                        }
                        else if (selectedGridEntry != null && selectedGridEntry.Expandable) {
                           SetExpand(selectedGridEntry, !selectedGridEntry.InternalExpanded);
                           return true;
                        }
                        break;
                }
            }
            return base.ProcessDialogKey(keyData);
        }
 
        protected virtual void RecalculateProps() {
            Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose,  "PropertyGridView:RecalculateProps");
            int props = CountPropsFromOutline(topLevelGridEntries);
            if (totalProps != props) {
                totalProps = props;
                ClearGridEntryEvents(allGridEntries, 0, -1);
                allGridEntries = null;
            }
        }
 
        internal /*public virtual*/ void RecursivelyExpand(GridEntry gridEntry, bool fInit, bool expand, int maxExpands) {
 
            if (gridEntry == null || (expand && --maxExpands < 0)) {
                return;
            }
 
            SetExpand(gridEntry, expand);
 
            GridEntryCollection rgipes = gridEntry.Children;
            if (rgipes != null)
                for (int i = 0; i < rgipes.Count; i++)
                    RecursivelyExpand(rgipes.GetEntry(i),false, expand, maxExpands);
 
            if (fInit) {
                GridEntry ipeSelect = selectedGridEntry;
                Refresh();
                SelectGridEntry(ipeSelect,false);
                Invalidate();
            }
 
        }
 
        public override void Refresh() {
            Refresh(false, -1, -1);
 
            //resetting gridoutline rect to recalculate before repaint when viewsort property changed. This is necessary especially when user 
            // changes sort and move to a secondary monitor with different DPI and change view sort back to original.
            if (topLevelGridEntries != null && DpiHelper.EnableDpiChangedHighDpiImprovements) {
                var outlineRectIconSize = this.GetOutlineIconSize();
                foreach (GridEntry gridentry in topLevelGridEntries) {
                    if (gridentry.OutlineRect.Height != outlineRectIconSize || gridentry.OutlineRect.Width != outlineRectIconSize) {
                        ResetOutline(gridentry);
                    }
                }
            }
 
            // make sure we got everything
            Invalidate();
        }
 
        public void Refresh(bool fullRefresh) {
            Refresh(fullRefresh, -1, -1);
        }
 
        
        GridPositionData positionData;
    
        private void Refresh(bool fullRefresh, int rowStart, int rowEnd) {
            Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose,  "PropertyGridView:Refresh");
            Debug.WriteLineIf(GridViewDebugPaint.TraceVerbose,  "Refresh called for rows " + rowStart.ToString(CultureInfo.InvariantCulture) + " through " + rowEnd.ToString(CultureInfo.InvariantCulture));
            SetFlag(FlagNeedsRefresh, true);
            GridEntry gridEntry = null;
 
            // VSWhidbey 361345 -- there are cases here where the grid could get be disposed.
            // so just bail.
            if (this.IsDisposed) {
                return;
            }
 
            bool pageInGridEntry = true;
            
            if (rowStart == -1) {
                rowStart = 0;
            }
            
            if (fullRefresh || this.ownerGrid.HavePropEntriesChanged()) {
                if (HasEntries && !GetInPropertySet() && !Commit()) {
                    OnEscape(this);
                }
                
                int oldLength = totalProps;
                object oldObject = topLevelGridEntries == null || topLevelGridEntries.Count == 0 ? null : ((GridEntry)topLevelGridEntries[0]).GetValueOwner();
                
                // walk up to the main IPE and refresh it.
                if (fullRefresh) {
                    this.ownerGrid.RefreshProperties(true);
                }
                
                if (oldLength > 0 && !GetFlag(FlagNoDefault)) {
                     positionData = CaptureGridPositionData();
                     CommonEditorHide(true);
                }
 
                UpdateHelpAttributes(selectedGridEntry, null);
                selectedGridEntry = null;
                SetFlag(FlagIsNewSelection, true);
                topLevelGridEntries = this.ownerGrid.GetPropEntries();
                
                
                ClearGridEntryEvents(allGridEntries, 0, -1);
                allGridEntries = null;
                RecalculateProps();
                
                int newLength = totalProps;
                if (newLength > 0) {
                    if (newLength < oldLength) {
                        SetScrollbarLength();
                        SetScrollOffset(0);
                    }
                   
                    SetConstants();
 
                    if (positionData != null) {
                        gridEntry = positionData.Restore(this);
 
                        // Upon restoring the grid entry position, we don't
                        // want to page it in
                        //
                        object newObject = topLevelGridEntries == null || topLevelGridEntries.Count == 0 ? null : ((GridEntry)topLevelGridEntries[0]).GetValueOwner();
                        pageInGridEntry = (gridEntry == null) || oldLength != newLength || newObject != oldObject;
                    }
                   
                    if (gridEntry == null) {
                        gridEntry = this.ownerGrid.GetDefaultGridEntry();
                        SetFlag(FlagNoDefault, gridEntry == null && totalProps > 0);
                    }
 
                    InvalidateRows(rowStart, rowEnd);
                    if (gridEntry == null) {
                        selectedRow = 0;
                        selectedGridEntry = GetGridEntryFromRow(selectedRow);
                    }
                }
                else if (oldLength == 0) {
                    return;
                }
                else {
                    SetConstants();
                }
 
 
                // Release the old positionData which contains reference to previous selected objects.
                positionData = null;
                lastClickedEntry = null;
            }
 
            if (!HasEntries) {
                CommonEditorHide(selectedRow != -1);
                this.ownerGrid.SetStatusBox(null, null);
                SetScrollOffset(0);
                selectedRow = -1;
                Invalidate();
                return;
            }
            // in case we added or removed properties
            
            ownerGrid.ClearValueCaches();
 
            InvalidateRows(rowStart, rowEnd);
 
            if (gridEntry != null) {
                SelectGridEntry(gridEntry, pageInGridEntry);
            }
        }
 
        public virtual void Reset() {
            Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose,  "PropertyGridView:Reset");
            GridEntry gridEntry = GetGridEntryFromRow(selectedRow);
            if (gridEntry == null) return;
 
            gridEntry.ResetPropertyValue();
            SelectRow(selectedRow);
        }
 
        protected virtual void ResetOrigin(System.Drawing.Graphics g) {
            g.ResetTransform();
        }
        
        internal void RestoreHierarchyState(ArrayList expandedItems) {
            if (expandedItems == null) {
               return;
            }
            
            foreach(GridEntryCollection gec in expandedItems) {
               FindEquivalentGridEntry(gec);
            }
        }
 
        public virtual DialogResult RunDialog(Form dialog) {
            return ShowDialog(dialog);
        }
        
        internal ArrayList SaveHierarchyState(GridEntryCollection entries) {
            return SaveHierarchyState(entries, null);
        }
        
        private ArrayList SaveHierarchyState(GridEntryCollection entries, ArrayList expandedItems) {
            if (entries == null) {
               return new ArrayList();
            }
            
            if (expandedItems == null) {
               expandedItems = new ArrayList();
            }
        
            for (int i = 0; i < entries.Count; i++) {
               if (((GridEntry)entries[i]).InternalExpanded) {
                  GridEntry entry = entries.GetEntry(i);
                  expandedItems.Add(GetGridEntryHierarchy(entry.Children.GetEntry(0)));
                  SaveHierarchyState(entry.Children, expandedItems);
               }
            }
            
            return expandedItems;
        }
 
        // Scroll to the new offset
        private bool ScrollRows(int newOffset) {
 
             GridEntry ipeCur = selectedGridEntry;
   
             if (!IsScrollValueValid(newOffset) || !Commit()) {
                 return false;
             }
                   
             bool showEdit = Edit.Visible;
             bool showBtnDropDown = DropDownButton.Visible;
             bool showBtnEdit     = DialogButton.Visible;
                 
             Edit.Visible = false;
             DialogButton.Visible = false;
             DropDownButton.Visible = false;
                  
             SetScrollOffset(newOffset); 
             
             if (ipeCur != null) {
                int curRow = GetRowFromGridEntry(ipeCur);
                if (curRow >=0 && curRow < visibleRows-1) {
                    Edit.Visible = showEdit;
                    DialogButton.Visible = showBtnEdit;
                    DropDownButton.Visible = showBtnDropDown;
                    SelectGridEntry(ipeCur, true);
                }
                else {
                    CommonEditorHide();
                }
             }
             else {
                CommonEditorHide();
             }
             
             Invalidate();
             return true;
        }
 
        private void SelectEdit(bool caretAtEnd) {
            if (edit != null) {
                Edit.SelectAll();
            }
        }
 
        // select functions... selectGridEntry and selectRow will select a Row
        // and install the appropriate editors.
        //
        internal /*protected virtual*/ void SelectGridEntry(GridEntry gridEntry, bool fPageIn) {
 
            if (gridEntry == null) return;
            Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose,  "PropertyGridView:SelectGridEntry(" + gridEntry.PropertyLabel + ")");
 
            int row = GetRowFromGridEntry(gridEntry);
            if (row + GetScrollOffset() < 0) {
                // throw exception? return false?
                return;
            }
 
            int maxRows = (int)Math.Ceiling(((double)GetOurSize().Height)/(1+RowHeight));
 
            // Determine whether or not we need to page-in this GridEntry
            //
 
            if (!fPageIn || (row >= 0 && row < (maxRows-1))) {
                // No need to page-in: either fPageIn is false or the row is already in view
                //
                SelectRow(row);               
            }
            else {
                // Page-in the selected GridEntry
                //
                            
                selectedRow = -1; // clear the selected row since it's no longer a valid number
            
                int cOffset = GetScrollOffset();
                if (row < 0) {
                    SetScrollOffset(row + cOffset);
                    Invalidate();
                    SelectRow(0);
                }
                else {
                    // try to put it one row up from the bottom
                    int newOffset = row + cOffset - (maxRows - 2);
    
                    if (newOffset >= ScrollBar.Minimum && newOffset < ScrollBar.Maximum) {
                        SetScrollOffset(newOffset);
                    }
                    Invalidate();
                    SelectGridEntry(gridEntry, false);
                }
            }            
        }
 
        private void SelectRow(int row) {
 
            Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose,  "PropertyGridView:SelectRow(" + row.ToString(CultureInfo.InvariantCulture) + ")");
 
            if (!GetFlag(FlagIsNewSelection)) {
                if (this.FocusInside) {
                    // If we're in an error state, we want to bail out of this.
                    if (errorState != ERROR_NONE || (row != selectedRow && !Commit())) {
                        return;
                    }
                }
                else {
                    FocusInternal();
                }
            }
 
            GridEntry gridEntry = GetGridEntryFromRow(row);
 
            // Update our reset command.
            //
            if (row != selectedRow) {
                UpdateResetCommand(gridEntry);
            }
 
            if (GetFlag(FlagIsNewSelection) && GetGridEntryFromRow(selectedRow) == null) {
                CommonEditorHide();
            }
 
            UpdateHelpAttributes(selectedGridEntry, gridEntry);
 
            // tell the old selection it's not focused any more
            if (selectedGridEntry != null) {
                selectedGridEntry.Focus = false;
            }
 
            // selection not visible.
            if (row < 0 || row >= visibleRows) {
                CommonEditorHide();
                selectedRow = row;
                selectedGridEntry = gridEntry;
                Refresh();
                return;
            }
 
            // leave current selection.
            if (gridEntry == null)
                return;
 
            bool newRow = false;
            int oldSel = selectedRow;
            if (selectedRow != row || !gridEntry.Equals(selectedGridEntry)) {
                CommonEditorHide();
                newRow = true;
            }
            
            if (!newRow)
                CloseDropDown();
 
            Rectangle rect = GetRectangle(row,ROWVALUE);
            string s = gridEntry.GetPropertyTextValue();
 
            // what components are we using?
            bool fBtnDropDown = gridEntry.NeedsDropDownButton | gridEntry.Enumerable;
            bool fBtnDialog = gridEntry.NeedsCustomEditorButton;
            bool fEdit = gridEntry.IsTextEditable;
            bool fPaint = gridEntry.IsCustomPaint;
 
            rect.X += 1;
            rect.Width -= 1;
 
            // we want to allow builders on read-only properties
            if ((fBtnDialog || fBtnDropDown) && !gridEntry.ShouldRenderReadOnly && FocusInside) {
                Control btn = fBtnDropDown ? (Control)DropDownButton : (Control)DialogButton;
                Size sizeBtn = DpiHelper.EnableDpiChangedHighDpiImprovements? new Size(SystemInformation.VerticalScrollBarArrowHeightForDpi(this.deviceDpi), RowHeight):
                                                                              new Size(SystemInformation.VerticalScrollBarArrowHeight, RowHeight);
                Rectangle rectTarget = new Rectangle(rect.X+rect.Width-sizeBtn.Width,
                                                      rect.Y,
                                                      sizeBtn.Width,rect.Height);
                CommonEditorUse(btn,rectTarget);
                sizeBtn = btn.Size;
                rect.Width -= (sizeBtn.Width);
                btn.Invalidate();
            }
 
            // if we're painting the value, size the rect between the button and the painted value
            if (fPaint) {
                rect.X += paintIndent + 1;
                rect.Width -= paintIndent + 1;
            }
            else {
                rect.X += EDIT_INDENT + 1; // +1 to compensate for where GDI+ draws it's string relative to the rect.
                rect.Width -= EDIT_INDENT + 1;
            }
           
            if ((GetFlag(FlagIsNewSelection) || !Edit.Focused) && (s != null && !s.Equals(Edit.Text))) {
                Edit.Text = s;
                originalTextValue = s;
                Edit.SelectionStart = 0;
                Edit.SelectionLength = 0;
            }
            Edit.AccessibleName = gridEntry.Label;
 
#if true // RENDERMODE
            switch (inheritRenderMode) {
                case RENDERMODE_BOLD:
                    if (gridEntry.ShouldSerializePropertyValue()) {
                        Edit.Font = GetBoldFont();
                    }
                    else {
                        Edit.Font = Font;
                    }
                    break;
                case RENDERMODE_LEFTDOT:
                    if (gridEntry.ShouldSerializePropertyValue()) {
                        rect.X += (LEFTDOT_SIZE * 2);
                        rect.Width -= (LEFTDOT_SIZE * 2);
                    }
                    // nothing
                    break;
                case RENDERMODE_TRIANGLE:
                    // nothing
                    break;
            }
#endif
 
            if (GetFlag(FlagIsSplitterMove) || !gridEntry.HasValue || !FocusInside) {
                Edit.Visible = false;
            }
            else {
                rect.Offset(1,1);
                rect.Height -= 1;
                rect.Width -= 1;
                CommonEditorUse(Edit,rect);
                bool drawReadOnly = gridEntry.ShouldRenderReadOnly;
                Edit.ForeColor = drawReadOnly ? this.GrayTextColor : this.ForeColor;
                Edit.BackColor = this.BackColor;
                Edit.ReadOnly = drawReadOnly || !gridEntry.IsTextEditable;
                Edit.UseSystemPasswordChar = gridEntry.ShouldRenderPassword;
            }
 
            GridEntry oldSelectedGridEntry = selectedGridEntry;
            selectedRow = row;
            selectedGridEntry = gridEntry;
            this.ownerGrid.SetStatusBox(gridEntry.PropertyLabel,gridEntry.PropertyDescription);
 
            // tell the new focused item that it now has focus
            if (selectedGridEntry != null) {
                selectedGridEntry.Focus = this.FocusInside;
            }
 
            if (!GetFlag(FlagIsNewSelection)) {
                FocusInternal();
            }
            
            // 
 
            InvalidateRow(oldSel);
 
            InvalidateRow(row);
            if (FocusInside)
            {
                SetFlag(FlagIsNewSelection, false);
            }
            
            try {
                if (selectedGridEntry != oldSelectedGridEntry) {
                    this.ownerGrid.OnSelectedGridItemChanged(oldSelectedGridEntry, selectedGridEntry);
                }               
            }
            catch {
            }
        }
 
        public virtual void SetConstants() {
            Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose,  "PropertyGridView:SetConstants");
            Size size = this.GetOurSize();
            
            visibleRows = (int)Math.Ceiling(((double)size.Height)/(1+RowHeight));
            
            size = this.GetOurSize();
            
            if (size.Width >= 0) {
                labelRatio = Math.Max(Math.Min(labelRatio, 9), 1.1);
                labelWidth = ptOurLocation.X + (int) ((double)size.Width / labelRatio);
            }
            
            int oldWidth = labelWidth;
 
            
            bool adjustWidth = SetScrollbarLength();
            GridEntryCollection rgipesAll = GetAllGridEntries();
            if (rgipesAll != null) {
                int scroll = GetScrollOffset();
                if ((scroll + visibleRows) >= rgipesAll.Count) {
                    visibleRows = rgipesAll.Count - scroll;
                }
            }
            
            
            if (adjustWidth && size.Width >= 0) {
                labelRatio = ((double) GetOurSize().Width / (double) (oldWidth - ptOurLocation.X));
                //labelWidth = loc.X + (int) ((double)size.Width / labelRatio);
            }
 
            Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose,  "\tsize       :" + size.ToString());
            Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose,  "\tlocation   :" + ptOurLocation.ToString());
            Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose,  "\tvisibleRows:" + (visibleRows).ToString(CultureInfo.InvariantCulture));
            Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose,  "\tlabelWidth :" + (labelWidth).ToString(CultureInfo.InvariantCulture));
            Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose,  "\tlabelRatio :" + (labelRatio).ToString(CultureInfo.InvariantCulture));
            Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose,  "\trowHeight  :" + (RowHeight).ToString(CultureInfo.InvariantCulture));
#if DEBUG
            if (rgipesAll == null) {
                Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose,  "\tIPE Count  :(null)");
            }
            else {
                Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose,  "\tIPE Count  :" + (rgipesAll.Count).ToString(CultureInfo.InvariantCulture));
            }
#endif
        }
 
        private void SetCommitError(short error) {
            SetCommitError(error, error == ERROR_THROWN);
        }
 
        private void SetCommitError(short error, bool capture) {
        #if DEBUG
            if (CompModSwitches.DebugGridView.TraceVerbose) {
                string err = "UNKNOWN!";
                switch (error) {
                    case ERROR_NONE:
                        err = "ERROR_NONE";
                        break;
                    case ERROR_THROWN:
                        err = "ERROR_THROWN";
                        break;
                    case ERROR_MSGBOX_UP:
                        err = "ERROR_MSGBOX_UP";
                        break;
                }
                Debug.WriteLine( "PropertyGridView:SetCommitError(error=" + err + ", capture=" + capture.ToString() + ")");
            }
        #endif
            errorState = error;
            if (error != ERROR_NONE) {
                CancelSplitterMove();
            }
            
            Edit.HookMouseDown = capture;
            
        }
 
        internal /*public virtual*/ void SetExpand(GridEntry gridEntry, bool value) {
            if (gridEntry != null && gridEntry.Expandable) {
 
                int row = GetRowFromGridEntry(gridEntry);
                int countFromEnd = visibleRows - row;
                int curRow = selectedRow;
 
                // if the currently selected row is below us, we need to commit now
                // or the offsets will be wrong
                if (selectedRow != -1 && row < selectedRow && Edit.Visible) {
                    // this will cause the commit
                    FocusInternal();
 
                }
 
                int offset = GetScrollOffset();
                int items = totalProps;
 
                gridEntry.InternalExpanded = value;
                RecalculateProps();
                GridEntry ipeSelect = selectedGridEntry;
                if (!value) {
                    for (GridEntry ipeCur = ipeSelect; ipeCur != null; ipeCur = ipeCur.ParentGridEntry) {
                        if (ipeCur.Equals(gridEntry)) {
                            ipeSelect = gridEntry;
                        }
                    }
                }
                row = GetRowFromGridEntry(gridEntry);
 
                SetConstants();
 
                int newItems = totalProps - items;
 
                if (value && newItems > 0 && newItems < visibleRows && (row + (newItems)) >= visibleRows && newItems < curRow) {
                    // scroll to show the newly opened items.
                    SetScrollOffset((totalProps - items) + offset);
                }
 
                Invalidate();
 
                SelectGridEntry(ipeSelect,false);
 
                int scroll = GetScrollOffset();
                SetScrollOffset(0);
                SetConstants();
                SetScrollOffset(scroll);
            }
        }
 
        private void SetFlag(short flag, bool value) {
            if (value) {
                flags = (short)((ushort)flags|(ushort)flag);
            }
            else {
                flags &= (short)~flag;
            }
        }
 
        public virtual void SetScrollOffset(int cOffset) {
            Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose,  "PropertyGridView:SetScrollOffset(" + cOffset.ToString(CultureInfo.InvariantCulture) + ")");
            int posNew = Math.Max(0, Math.Min(totalProps - visibleRows + 1, cOffset));
            int posOld = ScrollBar.Value;
            if (posNew != posOld && IsScrollValueValid(posNew) && visibleRows > 0) {
                ScrollBar.Value = posNew;
                Invalidate(); 
                selectedRow = GetRowFromGridEntry(selectedGridEntry);
            }
        }
 
        // C#r
        internal virtual bool _Commit() {
            return Commit();
        }
 
        private bool Commit() {
 
            Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose,  "PropertyGridView:Commit()");
 
            if (errorState == ERROR_MSGBOX_UP) {
                Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose,  "PropertyGridView:Commit() returning false because an error has been thrown or we are in a property set");
                return false;
            }
 
            if (!this.NeedsCommit) {
                SetCommitError(ERROR_NONE);
                Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose,  "PropertyGridView:Commit() returning true because no change has been made");
                return true;
            }
 
            if (GetInPropertySet()) {
                return false;
            }
 
            GridEntry ipeCur = GetGridEntryFromRow(selectedRow);
            if (ipeCur == null) {
                return true;
            }
            bool success = false;
            try {
                success = CommitText(Edit.Text);
            }
            finally { 
    
                if (!success) {
                    Edit.FocusInternal();
                    SelectEdit(false);
                }
                else {
                    SetCommitError(ERROR_NONE);   
                }
            }
            return success;
        }
 
        private bool CommitValue(object value) {
            GridEntry ipeCur = selectedGridEntry;
 
            if (selectedGridEntry == null && selectedRow != -1) {
                ipeCur = GetGridEntryFromRow(selectedRow);
            }
 
            if (ipeCur == null) {
                Debug.Fail("Committing with no selected row!");
                return true;
            }
 
            return CommitValue(ipeCur, value);
        }
 
        internal bool CommitValue(GridEntry ipeCur, object value, bool closeDropDown = true) {
 
            Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose,  "PropertyGridView:CommitValue(" + (value==null ? "null" :value.ToString()) + ")");
 
            int propCount = ipeCur.ChildCount;
            bool capture = Edit.HookMouseDown;
            object originalValue = null;
 
            try {
                originalValue = ipeCur.PropertyValue;
            }
            catch {
                // if the getter is failing, we still want to let
                // the set happen.
            }
 
            try {
                try {
                    SetFlag(FlagInPropertySet, true);
 
                    //if this propentry is enumerable, then once a value is selected from the editor,
                    //we'll want to close the drop down (like true/false).  Otherwise, if we're
                    //working with Anchor for ex., then we should be able to select different values
                    //from the editor, without having it close every time.
                    if (ipeCur != null &&
                        ipeCur.Enumerable &&
                        closeDropDown) {
                           CloseDropDown();
                    }
 
                    try {
                        Edit.DisableMouseHook = true;
                        ipeCur.PropertyValue = value;
                    }
                    finally {
                        Edit.DisableMouseHook = false;
                        Edit.HookMouseDown = capture;
                    }
                }
                catch (Exception ex) {
                    SetCommitError(ERROR_THROWN);
                    ShowInvalidMessage(ipeCur.PropertyLabel, value, ex);
                    return false;
                }
            }
            finally {
                SetFlag(FlagInPropertySet, false);
            }
 
            SetCommitError(ERROR_NONE);
 
            string text = ipeCur.GetPropertyTextValue();
            if (!String.Equals(text, Edit.Text)) {
                Edit.Text = text;
                Edit.SelectionStart = 0;
                Edit.SelectionLength = 0;
            }
            originalTextValue = text;
 
            // Update our reset command.
            //
            UpdateResetCommand(ipeCur);
 
            if (ipeCur.ChildCount != propCount) {
                ClearGridEntryEvents(allGridEntries, 0, -1);
                allGridEntries = null;
                SelectGridEntry(ipeCur, true);
            }
 
            // in case this guy got disposed...
            if (ipeCur.Disposed) {
                bool editfocused = (edit != null && edit.Focused);
                
                // reselect the row to find the replacement.
                //
                SelectGridEntry(ipeCur, true);
                ipeCur = selectedGridEntry;
 
                if (editfocused && edit != null) {
                    edit.Focus();
                }
            }
 
            this.ownerGrid.OnPropertyValueSet(ipeCur, originalValue);
 
            return true;
        }
 
        private bool CommitText(string text) {
 
            Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose,  "PropertyGridView:CommitValue(" + (text==null ? "null" :text.ToString()) + ")");
 
            object value = null;
 
            GridEntry ipeCur = selectedGridEntry;
 
            if (selectedGridEntry == null && selectedRow != -1) {
                ipeCur = GetGridEntryFromRow(selectedRow);
            }
 
            if (ipeCur == null) {
                Debug.Fail("Committing with no selected row!");
                return true;
            }
 
            try
            {
                value = ipeCur.ConvertTextToValue(text);
            }
            catch (Exception ex)
            {
                SetCommitError(ERROR_THROWN);
                ShowInvalidMessage(ipeCur.PropertyLabel, text, ex);
                return false;
            }
 
            SetCommitError(ERROR_NONE);
 
            return CommitValue(value);
        }
        
        internal void ReverseFocus() {
            if (selectedGridEntry == null) {
               FocusInternal();
            }
            else {
               SelectGridEntry(selectedGridEntry, true);
               
               if (DialogButton.Visible) {
                  DialogButton.FocusInternal();
               }
               else if (DropDownButton.Visible) {
                  DropDownButton.FocusInternal();
               }
               else if (Edit.Visible) {
                  Edit.SelectAll();
                  Edit.FocusInternal();
               }
            }
        }
 
        private bool SetScrollbarLength() {
            Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose,  "PropertyGridView:SetScrollBarLength");
            bool sbChange = false;
            if (totalProps != -1) {
                if (totalProps < visibleRows) {
                    SetScrollOffset(0);
                }
                else if (GetScrollOffset() > totalProps) {
                    SetScrollOffset((totalProps+1) - visibleRows);
                }
 
                bool fHidden = !ScrollBar.Visible;
                if (visibleRows > 0) {
                    ScrollBar.LargeChange = visibleRows-1;
                }
                ScrollBar.Maximum = Math.Max(0,totalProps - 1);
                if (fHidden != (totalProps < visibleRows)) {
                    sbChange = true;
                    ScrollBar.Visible = fHidden;
                    Size size = GetOurSize();
                    if (labelWidth != -1 && size.Width > 0) {
                        if (labelWidth > ptOurLocation.X + size.Width) {
                            labelWidth = ptOurLocation.X + (int) ((double)size.Width / labelRatio);
                        }
                        else {
                            labelRatio = ((double) GetOurSize().Width / (double) (labelWidth - ptOurLocation.X));
                        }
                    }
                    Invalidate();
                }
            }
            return sbChange;
        }
 
        /// <include file='doc\PropertyGridView.uex' path='docs/doc[@for="PropertyGridView.ShowDialog"]/*' />
        /// <devdoc>
        ///      Shows the given dialog, and returns its dialog result.  You should always
        ///      use this method rather than showing the dialog directly, as this will
        ///      properly position the dialog and provide it a dialog owner.
        /// </devdoc>
        public DialogResult /* IWindowsFormsEditorService. */ ShowDialog(Form dialog) {
            // try to shift down if sitting right on top of existing owner.
            if (dialog.StartPosition == FormStartPosition.CenterScreen) {
                Control topControl = this;
                if (topControl != null) {
                    while (topControl.ParentInternal != null) {
                        topControl = topControl.ParentInternal;
                    }
                    if (topControl.Size.Equals(dialog.Size)) {
                        dialog.StartPosition = FormStartPosition.Manual;
                        Point location = topControl.Location;
                        // 
                        location.Offset(25, 25);
                        dialog.Location = location;
                    }
                }
            }
 
            IntPtr priorFocus = UnsafeNativeMethods.GetFocus();
 
            IUIService service = (IUIService)GetService(typeof(IUIService));
            DialogResult result;
            if (service != null) {
                result = service.ShowDialog(dialog);
            }
            else {
                result = dialog.ShowDialog(this);
            }
 
            if (priorFocus != IntPtr.Zero) {
                UnsafeNativeMethods.SetFocus(new HandleRef(null, priorFocus));
            }
 
            return result;
        }
 
        private void ShowFormatExceptionMessage(string propName, object value, Exception ex)
        {
            if (value == null)
            {
                value = "(null)";
            }
 
            if (propName == null)
            {
                propName = "(unknown)";
            }
 
            Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose, "PropertyGridView:ShowFormatExceptionMessage(prop=" + propName + ")");
 
            // we have to uninstall our hook so the user can push the button!
            bool hooked = Edit.HookMouseDown;
            Edit.DisableMouseHook = true;
            SetCommitError(ERROR_MSGBOX_UP, false);
 
            // Fix for NdpWhidbey#28082: Before invoking the error dialog, flush all mouse messages in the message queue.
            // Otherwise the click that triggered the error will still be in the queue, and will get eaten by the dialog,
            // potentially causing an accidental button click. Problem occurs because we trap clicks using a system hook,
            // which usually discards the message by returning 1 to GetMessage(). But this won't occur until after the
            // error dialog gets closed, which is much too late.
            NativeMethods.MSG mouseMsg = new NativeMethods.MSG();
            while (UnsafeNativeMethods.PeekMessage(ref mouseMsg,
                                                   NativeMethods.NullHandleRef,
                                                   NativeMethods.WM_MOUSEFIRST,
                                                   NativeMethods.WM_MOUSELAST,
                                                   NativeMethods.PM_REMOVE))
                ;
 
            // These things are just plain useless.
            //
            if (ex is System.Reflection.TargetInvocationException)
            {
                ex = ex.InnerException;
            }
 
            // Try to find an exception message to display
            //
            string exMessage = ex.Message;
 
            bool revert = false;
 
            while (exMessage == null || exMessage.Length == 0)
            {
                ex = ex.InnerException;
                if (ex == null)
                {
                    break;
                }
                exMessage = ex.Message;
            }
 
            IUIService uiSvc = (IUIService)GetService(typeof(IUIService));
 
            ErrorDialog.Message = SR.GetString(SR.PBRSFormatExceptionMessage);
            ErrorDialog.Text = SR.GetString(SR.PBRSErrorTitle);
            ErrorDialog.Details = exMessage;
 
 
            if (uiSvc != null)
            {
                revert = (DialogResult.Cancel == uiSvc.ShowDialog(ErrorDialog));
            }
            else
            {
                revert = (DialogResult.Cancel == this.ShowDialog(ErrorDialog));
            }
 
            Edit.DisableMouseHook = false;
 
            if (hooked)
            {
                SelectGridEntry(selectedGridEntry, true);
            }
            SetCommitError(ERROR_THROWN, hooked);
 
            if (revert)
            {
                OnEscape(Edit);
            }
        }
 
        internal void ShowInvalidMessage(string propName, object value, Exception ex) {
 
            if (value == null) {
                value = "(null)";
            }
 
            if (propName == null) {
                propName = "(unknown)";
            }
 
            Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose,  "PropertyGridView:ShowInvalidMessage(prop=" + propName + ")");
 
            // we have to uninstall our hook so the user can push the button!
            bool hooked = Edit.HookMouseDown;
            Edit.DisableMouseHook = true;
            SetCommitError(ERROR_MSGBOX_UP, false);
 
            // Fix for NdpWhidbey#28082: Before invoking the error dialog, flush all mouse messages in the message queue.
            // Otherwise the click that triggered the error will still be in the queue, and will get eaten by the dialog,
            // potentially causing an accidental button click. Problem occurs because we trap clicks using a system hook,
            // which usually discards the message by returning 1 to GetMessage(). But this won't occur until after the
            // error dialog gets closed, which is much too late.
            NativeMethods.MSG mouseMsg = new NativeMethods.MSG();
            while (UnsafeNativeMethods.PeekMessage(ref mouseMsg,
                                                   NativeMethods.NullHandleRef,
                                                   NativeMethods.WM_MOUSEFIRST,
                                                   NativeMethods.WM_MOUSELAST,
                                                   NativeMethods.PM_REMOVE))
                ;
 
            // These things are just plain useless.
            //
            if (ex is System.Reflection.TargetInvocationException) {
                ex = ex.InnerException;
            }
 
            // Try to find an exception message to display
            //
            string exMessage = ex.Message;
 
            bool revert = false;
 
            while (exMessage == null || exMessage.Length == 0) {
                ex = ex.InnerException;
                if (ex == null) {
                    break;
                }
                exMessage = ex.Message;
            }
            
            IUIService uiSvc = (IUIService)GetService(typeof(IUIService));
 
            ErrorDialog.Message = SR.GetString(SR.PBRSErrorInvalidPropertyValue);
            ErrorDialog.Text = SR.GetString(SR.PBRSErrorTitle);
            ErrorDialog.Details = exMessage;
            
 
            if (uiSvc != null) {
                revert = (DialogResult.Cancel == uiSvc.ShowDialog(ErrorDialog));
            }
            else {
                revert = (DialogResult.Cancel == this.ShowDialog(ErrorDialog));
            }
            
            Edit.DisableMouseHook = false;
 
            if (hooked) {
                SelectGridEntry(selectedGridEntry, true);
            }
            SetCommitError(ERROR_THROWN, hooked);
 
            if (revert) {
                OnEscape(Edit);
            }
        }
 
        private bool SplitterInside(int x, int y) {
            return(Math.Abs(x - InternalLabelWidth) < 4);
        }
 
        private void TabSelection() {
            GridEntry gridEntry = GetGridEntryFromRow(selectedRow);
            if (gridEntry == null)
                return;
 
            if (Edit.Visible) {
                Edit.FocusInternal();
                SelectEdit(false);
            }
            else if (dropDownHolder != null && dropDownHolder.Visible) {
                dropDownHolder.FocusComponent();
                return;
            }
            else if (currentEditor != null) {
                currentEditor.FocusInternal();
            }
 
            return;
        }
 
 
        internal void RemoveSelectedEntryHelpAttributes() {
            UpdateHelpAttributes(selectedGridEntry, null);
        }
        
        private void UpdateHelpAttributes(GridEntry oldEntry, GridEntry newEntry) {
            // Update the help context with the current property
            //
            IHelpService hsvc = GetHelpService();
            if (hsvc == null || oldEntry == newEntry) {
                return;
            }
 
            GridEntry temp = oldEntry;
            if (oldEntry != null && !oldEntry.Disposed) {
                
    
                while (temp != null) {
                    hsvc.RemoveContextAttribute("Keyword", temp.HelpKeyword);
                    temp = temp.ParentGridEntry;
                }
            }
 
            if (newEntry != null) {
                temp = newEntry;
            
                UpdateHelpAttributes(hsvc, temp, true);
            }
        }
        
        private void UpdateHelpAttributes(IHelpService helpSvc, GridEntry entry, bool addAsF1) {
            if (entry == null) {
               return;
            }
            
            UpdateHelpAttributes(helpSvc, entry.ParentGridEntry, false);
            string helpKeyword = entry.HelpKeyword;
            if (helpKeyword != null) {
               helpSvc.AddContextAttribute("Keyword", helpKeyword, addAsF1 ? HelpKeywordType.F1Keyword : HelpKeywordType.GeneralKeyword);
            }
        }
 
 
        private void UpdateUIBasedOnFont(bool layoutRequired) {
            if (IsHandleCreated && GetFlag(FlagNeedUpdateUIBasedOnFont)) {
                try {
                    if (listBox != null) {
                        DropDownListBox.ItemHeight = RowHeight + 2;
                    }
                    if (btnDropDown != null) {
                        if (DpiHelper.EnableDpiChangedHighDpiImprovements) {
                            btnDropDown.Size = new Size(SystemInformation.VerticalScrollBarArrowHeightForDpi(this.deviceDpi), RowHeight);
                        }
                        else {
                            btnDropDown.Size = new Size(SystemInformation.VerticalScrollBarArrowHeight, RowHeight);
                        }
                        if (btnDialog != null) {
                            DialogButton.Size = DropDownButton.Size;
                            if (DpiHelper.EnableDpiChangedHighDpiImprovements) {
                                btnDialog.Image = CreateResizedBitmap("dotdotdot.ico", DOTDOTDOT_ICONWIDTH, DOTDOTDOT_ICONHEIGHT);
                            }
                        }
                        if (DpiHelper.EnableDpiChangedHighDpiImprovements) {
                            btnDropDown.Image = CreateResizedBitmap("Arrow.ico", DOWNARROW_ICONWIDTH, DOWNARROW_ICONHEIGHT);
                        }
                    }
 
                    if (layoutRequired) {
                        LayoutWindow(true);
                    }
                }
                finally {
                    SetFlag(FlagNeedUpdateUIBasedOnFont, false);
                }
            }
        }
 
        private bool UnfocusSelection() {
            Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose,  "PropertyGridView:UnfocusSelection()");
            GridEntry gridEntry = GetGridEntryFromRow(selectedRow);
            if (gridEntry == null)
                return true;
 
            bool commit = Commit();
            
            if (commit && this.FocusInside) {
                FocusInternal();
            }
            return commit;
        }
 
        private void UpdateResetCommand(GridEntry gridEntry) {
            if (totalProps > 0) {
                IMenuCommandService mcs = (IMenuCommandService)GetService(typeof(IMenuCommandService));
                if (mcs != null) {
                    MenuCommand reset = mcs.FindCommand(PropertyGridCommands.Reset);
                    if (reset != null) {
                        reset.Enabled = gridEntry == null ? false : gridEntry.CanResetPropertyValue();
                    }
                }
            }
        }
 
        // a mini version of process dialog key
        // for responding to WM_GETDLGCODE
        internal bool WantsTab(bool forward) {
            if (forward) {
                if (this.Focused) {
                    // we want a tab if the grid has focus and
                    // we have a button or an Edit
                    if (DropDownButton.Visible ||
                        DialogButton.Visible ||
                        Edit.Visible) {
                        return true;
                    }
                }
                else if (Edit.Focused && (DropDownButton.Visible || DialogButton.Visible)) {
                    // if the Edit has focus, and we have a button, we want the tab as well
                    return true;
                }
                return ownerGrid.WantsTab(forward);
            }
            else {
                if (Edit.Focused || DropDownButton.Focused || DialogButton.Focused) {
                    return true;
                }
                return ownerGrid.WantsTab(forward);
            }
        }
        
        private unsafe bool WmNotify(ref Message m) {
            if (m.LParam != IntPtr.Zero) {
                NativeMethods.NMHDR* nmhdr = (NativeMethods.NMHDR*)m.LParam;
            
                if (nmhdr->hwndFrom == ToolTip.Handle) {
                    switch (nmhdr->code) {
                        case NativeMethods.TTN_POP:
                            break;
                        case NativeMethods.TTN_SHOW:
                            // we want to move the tooltip over where our text would be
                            Point mouseLoc = Cursor.Position;
    
                            // convert to window coords
                            mouseLoc = this.PointToClientInternal(mouseLoc);
    
                            // figure out where we are and apply the offset
                            mouseLoc = FindPosition(mouseLoc.X, mouseLoc.Y);
    
                            if (mouseLoc == InvalidPosition) {
                                break;
                            }
    
                            GridEntry curEntry = GetGridEntryFromRow(mouseLoc.Y);
    
                            if (curEntry == null) {
                                break;
                            }
    
                            // get the proper rectangle
                            Rectangle itemRect = GetRectangle(mouseLoc.Y, mouseLoc.X);
                            Point     tipPt    = Point.Empty;
    
                            // and if we need a tooltip, move the tooltip control to that point.
                            if (mouseLoc.X == ROWLABEL) {
                                tipPt = curEntry.GetLabelToolTipLocation(mouseLoc.X - itemRect.X, mouseLoc.Y - itemRect.Y);
                            }
                            else if (mouseLoc.X == ROWVALUE) {
                                tipPt = curEntry.ValueToolTipLocation;
                            }
                            else {
                                break;
                            }
    
                            if (tipPt != InvalidPoint) {
                                itemRect.Offset(tipPt);
                                ToolTip.PositionToolTip(this, itemRect);
                                m.Result = (IntPtr)1;
                                return true;
                            }
    
                            break;
                    }
                }
            }
            return false;
        }
        
        protected override void WndProc(ref Message m) {
            switch (m.Msg) {
                
                case NativeMethods.WM_SYSCOLORCHANGE:
                    Invalidate();
                    break;
 
                    // Microsoft -- if we get focus in the error
                    // state, make sure we push it back to the
                    // Edit or bad bad things can happen with
                    // our state...
                    //
                case NativeMethods.WM_SETFOCUS:
                    if (!GetInPropertySet() && Edit.Visible && (errorState != ERROR_NONE || !Commit())) {
                        base.WndProc(ref m);
                        Edit.FocusInternal();
                        return;
                    }
                    break;
                    
                case NativeMethods.WM_IME_STARTCOMPOSITION:
                    Edit.FocusInternal();
                    Edit.Clear();
                    UnsafeNativeMethods.PostMessage(new HandleRef(Edit, Edit.Handle), NativeMethods.WM_IME_STARTCOMPOSITION, 0, 0); 
                    return;
                    
                case NativeMethods.WM_IME_COMPOSITION:
                    Edit.FocusInternal();
                    UnsafeNativeMethods.PostMessage(new HandleRef(Edit, Edit.Handle), NativeMethods.WM_IME_COMPOSITION, m.WParam, m.LParam);
                    return;
                    
            case NativeMethods.WM_GETDLGCODE:
 
                    int flags = NativeMethods.DLGC_WANTCHARS |  NativeMethods.DLGC_WANTARROWS;
                    
 
                    if (selectedGridEntry != null) {
                        if ((ModifierKeys & Keys.Shift) == 0) {
                            // if we're going backwards, we don't want the tab.
                            // otherwise, we only want it if we have an edit...
                            //
                            if (edit.Visible) {
                                flags |= NativeMethods.DLGC_WANTTAB;
                            }
                        }
                    }
                    m.Result = (IntPtr)(flags);
                    return;
                    
                case NativeMethods.WM_MOUSEMOVE: 
                    
                    // check if it's the same position, of so eat the message
                    if (unchecked( (int) (long)m.LParam) == lastMouseMove) {
                        return;
                    }
                    lastMouseMove = unchecked( (int) (long)m.LParam);
                    break;
 
                case NativeMethods.WM_NOTIFY:
                    if (WmNotify(ref m))
                        return;
                    break;
                case AutomationMessages.PGM_GETSELECTEDROW:
                    m.Result = (IntPtr)GetRowFromGridEntry(selectedGridEntry);
                    return;
                case AutomationMessages.PGM_GETVISIBLEROWCOUNT:
                    m.Result = (IntPtr)Math.Min(visibleRows, totalProps);
                    return;
            }
 
            base.WndProc(ref m);
        }
 
        /// <summary>
        /// rescale constants for the DPI change
        /// </summary>
        /// <param name="deviceDpiOld"></param>
        /// <param name="deviceDpiNew"></param>
        protected override void RescaleConstantsForDpi(int deviceDpiOld, int deviceDpiNew) {
            base.RescaleConstantsForDpi(deviceDpiOld, deviceDpiNew);
            RescaleConstants();
        }
 
        /// <summary>
        /// Rescale constants on this object
        /// </summary>
        private void RescaleConstants() {
            if (DpiHelper.EnableDpiChangedHighDpiImprovements) {
                ClearCachedFontInfo();
                cachedRowHeight = -1;
                paintWidth = LogicalToDeviceUnits(PAINT_WIDTH);
                paintIndent = LogicalToDeviceUnits(PAINT_INDENT);
                outlineSizeExplorerTreeStyle = LogicalToDeviceUnits(OUTLINE_SIZE_EXPLORER_TREE_STYLE);
                outlineSize = LogicalToDeviceUnits(OUTLINE_SIZE);
                maxListBoxHeight = LogicalToDeviceUnits(MAX_LISTBOX_HEIGHT);
                offset_2Units = LogicalToDeviceUnits(OFFSET_2PIXELS);
                if (topLevelGridEntries != null) {
                    foreach (GridEntry t in topLevelGridEntries) {
                        ResetOutline(t);
                    }
                }
            }
        }
 
        /// <summary>
        /// private method to recursively reset outlinerect for grid entries ( both visible and invisible)
        /// </summary>
        private void ResetOutline(GridEntry entry) {
            entry.OutlineRect = Rectangle.Empty;
            if (entry.ChildCount > 0) {
                foreach (GridEntry ent in entry.Children) {
                    ResetOutline(ent);
                }
            }
            return;
        }
 
        private class DropDownHolder : Form, IMouseHookClient {
 
            private  Control                        currentControl = null; // the control that is hosted in the holder
            private  PropertyGridView               gridView;              // the owner gridview
            private  MouseHook                      mouseHook;             // we use this to hook mouse downs, etc. to know when to close the dropdown.
            
            private  LinkLabel                      createNewLink = null;
            
            // all the resizing goo...
            //
            private bool                            resizable       = true;  // true if we're showing the resize widget.
            private bool                            resizing        = false; // true if we're in the middle of a resize operation.
            private  bool                           resizeUp        = false; // true if the dropdown is above the grid row, which means the resize widget is at the top.
            private Point                           dragStart       = Point.Empty;     // the point at which the drag started to compute the delta
            private Rectangle                       dragBaseRect    = Rectangle.Empty; // the original bounds of our control.
            private int                             currentMoveType = MoveTypeNone;    // what type of move are we processing? left, bottom, or both?
        
            private int                             ResizeBarSize;    // the thickness of the resize bar
            private int                             ResizeBorderSize; // the thickness of the 2-way resize area along the outer edge of the resize bar
            private int                             ResizeGripSize;   // the size of the 4-way resize grip at outermost corner of the resize bar
            private Size                            MinDropDownSize;  // the minimum size for the control.
            private Bitmap                          sizeGripGlyph;    // our cached size grip glyph.  Control paint only does right bottom glyphs, so we cache a mirrored one.  See GetSizeGripGlyph
 
            private const int                       DropDownHolderBorder = 1;
            private const int                       MoveTypeNone    = 0x0;
            private const int                       MoveTypeBottom	= 0x1;
            private const int                       MoveTypeLeft	= 0x2;
            private const int                       MoveTypeTop  	= 0x4;
        
            [
                SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters") // DropDownHolder's caption is not visible.
                                                                                                            // So we don't have to localize its text.
            ]
            internal DropDownHolder(PropertyGridView psheet)
            : base() {
                
               MinDropDownSize = new Size(SystemInformation.VerticalScrollBarWidth * 4, SystemInformation.HorizontalScrollBarHeight * 4);
               ResizeGripSize = SystemInformation.HorizontalScrollBarHeight;
               ResizeBarSize = ResizeGripSize + 1;
               ResizeBorderSize = ResizeBarSize / 2;
 
               this.ShowInTaskbar = false;
               this.ControlBox = false;
               this.MinimizeBox = false;
               this.MaximizeBox = false;
               this.Text = "";
               this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
               this.AutoScaleMode = AutoScaleMode.None; // children may scale, but we won't interfere.
               mouseHook = new MouseHook(this, this, psheet);
               Visible = false;
               gridView = psheet;
               this.BackColor = gridView.BackColor;
            }
 
            protected override CreateParams CreateParams {
                get {
                    CreateParams cp = base.CreateParams;
                    cp.ExStyle |= NativeMethods.WS_EX_TOOLWINDOW;
                    cp.Style |= NativeMethods.WS_POPUP | NativeMethods.WS_BORDER;
                    if (OSFeature.IsPresent(SystemParameter.DropShadow)) {
                        cp.ClassStyle |= NativeMethods.CS_DROPSHADOW;
                    }
                    if (gridView != null) {
                        cp.Parent = gridView.ParentInternal.Handle;
                    }
                    return cp;
                }
            }
 
            private LinkLabel CreateNewLink {
                get {
                    if (this.createNewLink == null) {
                        this.createNewLink = new LinkLabel();
                        this.createNewLink.LinkClicked += new LinkLabelLinkClickedEventHandler(OnNewLinkClicked);
                    }
                    return createNewLink;
                }
            }
       
 
            public virtual bool HookMouseDown{
                get{
                    return mouseHook.HookMouseDown;
                }
                set{
                    mouseHook.HookMouseDown = value;
                }
            }
 
            /// <devdoc>
            /// This gets set to true if there isn't enough space below the currently selected
            /// row for the drop down, so it appears above the row.  In this case, we make the resize
            /// grip appear at the top left.
            /// </devdoc>
            public bool ResizeUp  {
           
                set {
                    if (resizeUp != value) {
 
                        // clear the glyph so we regenerate it.
                        //
                        sizeGripGlyph = null;
                        resizeUp = value;
 
                        if (resizable) {
                            this.DockPadding.Bottom = 0;
                            this.DockPadding.Top = 0;
                            if (value) {
                                this.DockPadding.Top = ResizeBarSize;
                            }
                            else {
                                this.DockPadding.Bottom = ResizeBarSize;
                            }
                        }
                    }
                }
            }
            
            
            protected override void DestroyHandle() {
                  mouseHook.HookMouseDown = false;
                  base.DestroyHandle();
            }
 
            protected override void Dispose(bool disposing) {
 
                if (disposing && createNewLink != null) {
                    createNewLink.Dispose();
                    createNewLink = null;
                }
                base.Dispose(disposing);
            }
            
            public void DoModalLoop() {
 
                // Push a modal loop.  This seems expensive, but I think it is a
                // better user model than returning from DropDownControl immediately.
                //  
                while (this.Visible) {
                    Application.DoEventsModal();
                    UnsafeNativeMethods.MsgWaitForMultipleObjectsEx(0, IntPtr.Zero, 250, NativeMethods.QS_ALLINPUT, NativeMethods.MWMO_INPUTAVAILABLE);
                }
            }
 
            public virtual Control Component {
                get {
                    return currentControl;
                }
            }
 
            /// <devdoc>
            /// Get an InstanceCreationEditor for this entry.  First, we look on the property type, and if we
            /// don't find that we'll go up to the editor type itself.  That way people can associate the InstanceCreationEditor with
            /// the type of DropDown UIType Editor.
            ///
            /// </devdoc>
            private InstanceCreationEditor GetInstanceCreationEditor(PropertyDescriptorGridEntry entry) {
 
                if (entry == null) {
                    return null;
                }
 
                InstanceCreationEditor editor = null;
 
                // check the property type itself.  this is the default path.
                //
                PropertyDescriptor pd = entry.PropertyDescriptor;
                if (pd != null) {
                    editor = pd.GetEditor(typeof(InstanceCreationEditor)) as InstanceCreationEditor;
                }
 
                // now check if there is a dropdown UI type editor.  If so, use that.
                //
                if (editor == null) {
                    UITypeEditor ute = entry.UITypeEditor;
                    if (ute != null && ute.GetEditStyle() == UITypeEditorEditStyle.DropDown) {
                        editor = (InstanceCreationEditor)TypeDescriptor.GetEditor(ute, typeof(InstanceCreationEditor));
                    }
                }
                return editor;
            }
 
            /// <devdoc>
            /// Get a glyph for sizing the lower left hand grip.  The code in ControlPaint only does lower-right glyphs
            /// so we do some GDI+ magic to take that glyph and mirror it.  That way we can still share the code (in case it changes for theming, etc),
            /// not have any special cases, and possibly solve world hunger.  
            /// </devdoc>
            [ResourceExposure(ResourceScope.Machine)]
            [ResourceConsumption(ResourceScope.Machine)]
            private Bitmap GetSizeGripGlyph(Graphics g) {
                if (this.sizeGripGlyph != null) {
                    return sizeGripGlyph;
                }
 
                // create our drawing surface based on the current graphics context.
                //
                sizeGripGlyph = new Bitmap(ResizeGripSize, ResizeGripSize, g);		
                
                using (Graphics glyphGraphics = Graphics.FromImage(sizeGripGlyph)) 
                {
                    // mirror the image around the x-axis to get a gripper handle that works
                    // for the lower left.
                    System.Drawing.Drawing2D.Matrix m = new System.Drawing.Drawing2D.Matrix();
        
                    // basically, mirroring is just scaling by -1 on the X-axis.  So any point that's like (10, 10) goes to (-10, 10). 
                    // that mirrors it, but also moves everything to the negative axis, so we just bump the whole thing over by it's width.
                    // 
                    // the +1 is because things at (0,0) stay at (0,0) since [0 * -1 = 0] so we want to get them over to the other side too.
                    //
                    // resizeUp causes the image to also be mirrored vertically so the grip can be used as a top-left grip instead of bottom-left.
                    //
                    m.Translate(ResizeGripSize + 1, (resizeUp ? ResizeGripSize + 1: 0));			
                    m.Scale(-1, (resizeUp ? -1 : 1));
                    glyphGraphics.Transform = m;
                    ControlPaint.DrawSizeGrip(glyphGraphics, BackColor, 0, 0, ResizeGripSize, ResizeGripSize);
                    glyphGraphics.ResetTransform();
                    
                }
                sizeGripGlyph.MakeTransparent(BackColor);
                return sizeGripGlyph;
            }
 
            public virtual bool GetUsed() {
                return(currentControl != null);
            }
            
            public virtual void FocusComponent() {
                Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose,  "DropDownHolder:FocusComponent()");
                if (currentControl != null && Visible) {
                    currentControl.FocusInternal();
                }
            }
            
            /// <devdoc>
            ///    General purpose method, based on Control.Contains()...
            ///
            ///    Determines whether a given window (specified using native window handle)
            ///    is a descendant of this control. This catches both contained descendants
            ///    and 'owned' windows such as modal dialogs. Using window handles rather
            ///    than Control objects allows it to catch un-managed windows as well.
            /// </devdoc>
            private bool OwnsWindow(IntPtr hWnd) {
                while (hWnd != IntPtr.Zero) {
                    hWnd = UnsafeNativeMethods.GetWindowLong(new HandleRef(null, hWnd), NativeMethods.GWL_HWNDPARENT);
                    if (hWnd == IntPtr.Zero) {
                        return false;
                    }
                    if (hWnd == this.Handle) {
                        return true;
                    }
                }
                return false;
            }
 
            public bool OnClickHooked() {
                  gridView.CloseDropDownInternal(false);
                  return false;
            }
            
            private void OnCurrentControlResize(object o, EventArgs e) {
                if (currentControl != null && !resizing) {
                    int oldWidth = this.Width;
                    Size newSize = new Size(2 * DropDownHolderBorder + currentControl.Width, 2 * DropDownHolderBorder + currentControl.Height);
                    if (resizable) {
                        newSize.Height += ResizeBarSize;
                    }
                    try {
                        resizing = true;
                        this.SuspendLayout();
                        this.Size = newSize;
                    }
                    finally {
                        resizing = false;
                        this.ResumeLayout(false);
                    }
                    this.Left -= (this.Width - oldWidth);
                }
            }
 
 
            protected override void OnLayout(LayoutEventArgs levent) {
                try {
                    resizing = true;
                    base.OnLayout(levent);
                }
                finally {
                    resizing = false;
                }
                
            }
 
            private void OnNewLinkClicked(object sender, LinkLabelLinkClickedEventArgs e) {
                InstanceCreationEditor ice = e.Link.LinkData as InstanceCreationEditor;
 
                Debug.Assert(ice != null, "How do we have a link without the InstanceCreationEditor?");
                if (ice != null) {
                    Type createType = gridView.SelectedGridEntry.PropertyType;
                    if (createType != null) {
 
                        gridView.CloseDropDown();
                        
                        object newValue = ice.CreateInstance(gridView.SelectedGridEntry, createType);
 
                        if (newValue != null) {
 
                            // make sure we got what we asked for.
                            //
                            if (!createType.IsInstanceOfType(newValue)) {
                                throw new InvalidCastException(SR.GetString(SR.PropertyGridViewEditorCreatedInvalidObject, createType));
                            }
 
                            gridView.CommitValue(newValue);
                        }
                    }
                }
            }
            
            /// <devdoc>
            /// Just figure out what kind of sizing we would do at a given drag location.
            /// </devdoc>
            private int MoveTypeFromPoint(int x, int y) 
            {
                Rectangle bGripRect = new Rectangle(0, Height - ResizeGripSize, ResizeGripSize, ResizeGripSize);
                Rectangle tGripRect = new Rectangle(0, 0, ResizeGripSize, ResizeGripSize);
                
                if (!resizeUp && bGripRect.Contains(x, y))
                {
                    return MoveTypeLeft | MoveTypeBottom;
                }
                else if (resizeUp && tGripRect.Contains(x, y)) {
                    return MoveTypeLeft | MoveTypeTop;
                }
                else if (!resizeUp && Math.Abs(Height - y) < ResizeBorderSize) 
                {
                    return MoveTypeBottom;				
                }
                else if (resizeUp && Math.Abs(y) < ResizeBorderSize) 
                {
                    return MoveTypeTop;				
                }
                return MoveTypeNone;
            }
    
            /// <devdoc>
            /// Decide if we're going to be sizing at the given point, and if so, Capture and safe our current state.
            /// </devdoc>
            protected override void OnMouseDown(MouseEventArgs e)
            {
                if (e.Button == MouseButtons.Left) {
                    this.currentMoveType = MoveTypeFromPoint(e.X, e.Y);
                    if (this.currentMoveType != MoveTypeNone) 
                    {
                        dragStart = PointToScreen(new Point(e.X, e.Y));
                        dragBaseRect = Bounds;
                        Capture = true;
                    }
                    else {
                        gridView.CloseDropDown();
                    }
                }
                base.OnMouseDown (e);
            }
    
            /// <devdoc>
            /// Either set the cursor or do a move, depending on what our currentMoveType is/
            /// </devdoc>
            protected override void OnMouseMove(MouseEventArgs e)
            {     
                // not moving so just set the cursor.
                //
                if (this.currentMoveType == MoveTypeNone) 
                {
                    int cursorMoveType = MoveTypeFromPoint(e.X, e.Y);
                    switch (cursorMoveType) {
                        case (MoveTypeLeft | MoveTypeBottom):
                            Cursor = Cursors.SizeNESW;
                            break;
                        case MoveTypeBottom:
                        case MoveTypeTop:
                            Cursor = Cursors.SizeNS;
                            break;
                        case MoveTypeTop | MoveTypeLeft:
                            Cursor = Cursors.SizeNWSE;
                            break;
                        default:
                            Cursor = null;
                            break;
                    }
                }
                else 
                {
                    Point dragPoint = PointToScreen(new Point(e.X, e.Y));
                    Rectangle newBounds = Bounds;
                    
                    // we're in a move operation, so do the resize.
                    //
                    if ((currentMoveType & MoveTypeBottom) == MoveTypeBottom) 
                    {
                        newBounds.Height = Math.Max(MinDropDownSize.Height, dragBaseRect.Height + (dragPoint.Y - dragStart.Y));
                    }
    
                    // for left and top moves, we actually have to resize and move the form simultaneously.
                    // do to that, we compute the xdelta, and apply that to the base rectangle if it's not going to
                    // make the form smaller than the minimum.
                    //
                    if ((currentMoveType & MoveTypeTop) == MoveTypeTop) 
                    {
                        int delta = dragPoint.Y - dragStart.Y;                  
    
                        if ((dragBaseRect.Height - delta) > MinDropDownSize.Height) 
                        {
                            newBounds.Y     = dragBaseRect.Top + delta;
                            newBounds.Height = dragBaseRect.Height - delta;
                        }
                    }
    
                    if ((currentMoveType & MoveTypeLeft) == MoveTypeLeft) 
                    {
                        int delta = dragPoint.X - dragStart.X;                  
    
                        if ((dragBaseRect.Width - delta) > MinDropDownSize.Width) 
                        {
                            newBounds.X     = dragBaseRect.Left + delta;
                            newBounds.Width = dragBaseRect.Width - delta;
                        }
                    }
    
                    if (newBounds != Bounds) {
                        try {
                            resizing = true;
                            this.Bounds = newBounds;
                        }
                        finally {
                            resizing = false;
                        }
                    }
    
                    // Redraw!
                    //
                    Invalidate();
                }
                base.OnMouseMove (e);
            }
    
            protected override void OnMouseLeave(EventArgs e)
            {
                // just clear the cursor back to the default.
                //
                Cursor = null;
                base.OnMouseLeave (e);
            }
    
            protected override void OnMouseUp(MouseEventArgs e)
            {
                base.OnMouseUp (e);
    
                if (e.Button == MouseButtons.Left) {
                    
                    // reset the world.
                    //
                    this.currentMoveType = MoveTypeNone;
                    this.dragStart = Point.Empty;
                    this.dragBaseRect = Rectangle.Empty;
                    this.Capture = false;
                }
            }
    
            /// <devdoc>
            /// Just paint and draw our glyph.
            /// </devdoc>
            protected override void OnPaint(PaintEventArgs pe) 
            {
                base.OnPaint(pe);
                if (resizable) {
                    // Draw the grip
                    Rectangle lRect = new Rectangle(0, resizeUp ? 0 : Height - ResizeGripSize, ResizeGripSize, ResizeGripSize);
                    pe.Graphics.DrawImage(GetSizeGripGlyph(pe.Graphics), lRect);
 
                    // Draw the divider
                    int y = resizeUp ? (ResizeBarSize - 1) : (Height - ResizeBarSize);
                    Pen pen = new Pen(SystemColors.ControlDark, 1);
                    pen.DashStyle = DashStyle.Solid;
                    pe.Graphics.DrawLine(pen, 0, y, Width, y);
                    pen.Dispose();
                }
            }
          
           protected override bool ProcessDialogKey(Keys keyData) {
                if ((keyData & (Keys.Shift | Keys.Control | Keys.Alt)) == 0) {
                    switch (keyData & Keys.KeyCode) {
                        case Keys.Escape:
                            gridView.OnEscape(this);
                            return true;
                        case Keys.F4:
                            gridView.F4Selection(true);
                            return true;
                        case Keys.Return:
                            // make sure the return gets forwarded to the control that
                            // is being displayed
                            if (gridView.UnfocusSelection() && gridView.SelectedGridEntry != null) {
                              gridView.SelectedGridEntry.OnValueReturnKey();
                            }
                            return true;
                    }
                }
 
                return base.ProcessDialogKey(keyData);
            }
 
            public void SetComponent(Control ctl, bool resizable) {
 
                this.resizable = resizable;
                this.Font = gridView.Font;
 
                // check to see if we're going to be adding an InstanceCreationEditor
                //
                InstanceCreationEditor editor = (ctl == null ? null : GetInstanceCreationEditor(gridView.SelectedGridEntry as PropertyDescriptorGridEntry));
 
                // clear any existing control we have
                //
                if (currentControl != null) {
                    currentControl.Resize -= new EventHandler(this.OnCurrentControlResize);
                    Controls.Remove(currentControl);
                    currentControl = null;
                }
 
                // remove the InstanceCreationEditor link
                //
                if (createNewLink != null && createNewLink.Parent == this) {
                    this.Controls.Remove(createNewLink);
                }
 
                // now set up the new control, top to bottom
                //
                if (ctl != null) {
 
                    currentControl = ctl;
                    Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose,  "DropDownHolder:SetComponent(" + (ctl.GetType().Name) + ")");
                    
                    this.DockPadding.All = 0;
 
                    // first handle the control.  If it's a listbox, make sure it's got some height
                    // to it.
                    //
                    if (currentControl is GridViewListBox) {
                        ListBox lb = (ListBox)currentControl;
 
                        if (lb.Items.Count == 0) {
                            lb.Height = Math.Max(lb.Height, lb.ItemHeight);
                        }
                    }
 
                    // Parent the control now.  That way it can inherit our
                    // font and scale itself if it wants to.
                    try {
                        SuspendLayout();
                        Controls.Add(ctl);
 
                        Size sz = new Size(2 * DropDownHolderBorder + ctl.Width, 2 * DropDownHolderBorder + ctl.Height);
    
                        // now check for an editor, and show the link if there is one.
                        //
                        if (editor != null) {
    
                            // set up the link.
                            //
                            CreateNewLink.Text = editor.Text;
                            CreateNewLink.Links.Clear();
                            CreateNewLink.Links.Add(0, editor.Text.Length, editor);
    
                            // size it as close to the size of the text as possible.
                            //
                            int linkHeight = CreateNewLink.Height;
                            using (Graphics g = gridView.CreateGraphics()) {
                                SizeF sizef = PropertyGrid.MeasureTextHelper.MeasureText(this.gridView.ownerGrid, g, editor.Text, gridView.GetBaseFont());
                                linkHeight = (int)sizef.Height;
                            }
                            
                            CreateNewLink.Height = linkHeight + DropDownHolderBorder;
    
                            // add the total height plus some border
                            sz.Height += (linkHeight + (DropDownHolderBorder * 2));
                        }
    
                        // finally, if we're resizable, add the space for the widget.
                        //
                        if (resizable) {
                            sz.Height += ResizeBarSize;
    
                            // we use dockpadding to save space to draw the widget.
                            //
                            if (resizeUp) {
                                this.DockPadding.Top = ResizeBarSize;
                            }
                            else {
                                this.DockPadding.Bottom = ResizeBarSize;
                            }
                        }
 
                        // set the size stuff.
                        //
                        Size = sz;
 
                        // DockStyle property on child control is forcing to resize to Parent control size that is not yet scaled.
                        // Parent control's Preffered size, by this time, is calculated based on child controls scaled dimensions.
                        // Also, making sure we are not shrinking if we are already there.
                        if(DpiHelper.EnableDpiChangedHighDpiImprovements) {
                            ctl.Visible = true;
                            if (this.Size.Height < this.PreferredSize.Height) {
                                this.Size = new Size(this.Size.Width, this.PreferredSize.Height);
                            }
                            ctl.Dock = DockStyle.Fill;
                        }
                        else {
                            ctl.Dock = DockStyle.Fill;
                            ctl.Visible = true;
                        }
 
                        if (editor != null) {
                            CreateNewLink.Dock = DockStyle.Bottom;
                            Controls.Add(CreateNewLink);
                        }
                    }
                    finally {
                        ResumeLayout(true);
                    }
 
                    // hook the resize event.
                    //
                    currentControl.Resize += new EventHandler(this.OnCurrentControlResize);
                }
                Enabled = currentControl != null;
            }
 
            protected override void WndProc(ref Message m) {
 
                if (m.Msg == NativeMethods.WM_ACTIVATE) {
                    SetState(STATE_MODAL, true);
                    Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose, "DropDownHolder:WM_ACTIVATE()");
                    IntPtr activatedWindow = (IntPtr)m.LParam;
                    if (Visible && NativeMethods.Util.LOWORD(m.WParam) == NativeMethods.WA_INACTIVE && !this.OwnsWindow(activatedWindow)) {
                        gridView.CloseDropDownInternal(false);
                        return;
                    }
 
                    // prevent the IMsoComponentManager active code from getting fired.
                    //Active = ((int)m.WParam & 0x0000FFFF) != NativeMethods.WA_INACTIVE;
                    //return;
                }
                else if (m.Msg == NativeMethods.WM_CLOSE) {
                    // don't let an ALT-F4 get you down
                    //
                    if (Visible) {
                        gridView.CloseDropDown();
                    }
                    return;
                }
                else if (m.Msg == NativeMethods.WM_DPICHANGED) {
                    if (DpiHelper.EnableDpiChangedHighDpiImprovements) {
                        // Dropdownholder in PropertyGridView is already scaled based on parent font and other properties that were already set for new DPI
                        // This case is to avoid rescaling(double scaling) of this form due to Font changes.
                        
                        var oldDpi = this.deviceDpi;
                        this.deviceDpi =
                            (int) UnsafeNativeMethods.GetDpiForWindow(new HandleRef(this, HandleInternal));
                        if (oldDpi != deviceDpi) {
                            RescaleConstantsForDpi(oldDpi, this.deviceDpi);
                            this.PerformLayout();
                        }
 
                        m.Result = IntPtr.Zero;
                        return;
                    }
                }
 
                base.WndProc(ref m);
            }
 
            protected override void RescaleConstantsForDpi(int oldDpi, int newDpi) {
                base.RescaleConstantsForDpi(oldDpi, newDpi);
                if (!DpiHelper.EnableDpiChangedHighDpiImprovements) {
                    return;
                }
 
                var scrollbarHeight = SystemInformation.GetHorizontalScrollBarHeightForDpi(newDpi);
                MinDropDownSize = new Size(SystemInformation.GetVerticalScrollBarWidthForDpi(newDpi) * 4, scrollbarHeight * 4);
                ResizeGripSize = scrollbarHeight;
 
                ResizeBarSize = ResizeGripSize + 1;
                ResizeBorderSize = ResizeBarSize / 2;
                
                var factor = (double)newDpi / oldDpi;
                this.Height = (int)Math.Round(factor * this.Height);
            }
 
        }
 
        private class GridViewListBox : ListBox {
 
            internal bool fInSetSelectedIndex = false;
            private PropertyGridView _owningPropertyGridView;
 
            public GridViewListBox(PropertyGridView gridView) {
                base.IntegralHeight = false;
                _owningPropertyGridView = gridView;
                base.BackColor = gridView.BackColor;
            }
 
            protected override CreateParams CreateParams {
                get {
                    CreateParams cp = base.CreateParams;
                    cp.Style &= ~NativeMethods.WS_BORDER;
                    cp.ExStyle &= ~NativeMethods.WS_EX_CLIENTEDGE;
                    return cp;
                }
            }
 
            /// <summary>
            /// Gets the owning PropertyGridView.
            /// </summary>
            internal PropertyGridView OwningPropertyGridView {
                get {
                    return _owningPropertyGridView;
                }
            }
 
            /// <summary>
            /// Indicates whether or not the control supports UIA Providers via
            /// IRawElementProviderFragment/IRawElementProviderFragmentRoot interfaces
            /// </summary>
            internal override bool SupportsUiaProviders {
                get {
                    return AccessibilityImprovements.Level3;
                }
            }
 
            /// <summary>
            /// Constructs the new instance of the accessibility object for this control.
            /// </summary>
            /// <returns>The accessibility object instance.</returns>
            protected override AccessibleObject CreateAccessibilityInstance() {
                if (AccessibilityImprovements.Level3) {
                    return new GridViewListBoxAccessibleObject(this);
                }
 
                return base.CreateAccessibilityInstance();
            }
 
            public virtual bool InSetSelectedIndex() {
                return fInSetSelectedIndex;
            }
 
            protected override void OnSelectedIndexChanged(EventArgs e) {
                fInSetSelectedIndex = true;
                base.OnSelectedIndexChanged(e);
                fInSetSelectedIndex = false;
 
                var gridViewListBoxAccessibleObject = AccessibilityObject as GridViewListBoxAccessibleObject;
                if (gridViewListBoxAccessibleObject != null) {
                    gridViewListBoxAccessibleObject.SetListBoxItemFocus();
                }
            }
        }
 
        [ComVisible(true)]
        private class GridViewListBoxItemAccessibleObject : AccessibleObject {
 
            private GridViewListBox _owningGridViewListBox;
            private object _owningItem;
 
            public GridViewListBoxItemAccessibleObject(GridViewListBox owningGridViewListBox, object owningItem) {
                _owningGridViewListBox = owningGridViewListBox;
                _owningItem = owningItem;
 
                UseStdAccessibleObjects(_owningGridViewListBox.Handle);
            }
 
            /// <summary>
            /// Gets the DropDown button bounds.
            /// </summary>
            public override Rectangle Bounds {
                get {
                    int left;
                    int top;
                    int width;
                    int height;
                    var systemIAccessible = GetSystemIAccessibleInternal();
                    systemIAccessible.accLocation(out left, out top, out width, out height, GetChildId());
                    return new Rectangle(left, top, width, height);
                }
            }
 
            /// <summary>
            /// Gets the DropDown button default action.
            /// </summary>
            public override string DefaultAction {
                get {
                    var systemIAccessible = GetSystemIAccessibleInternal();
                    return systemIAccessible.accDefaultAction[GetChildId()];
                }
            }
 
            internal override UnsafeNativeMethods.IRawElementProviderFragment FragmentNavigate(UnsafeNativeMethods.NavigateDirection direction) {
                switch (direction) {
                    case UnsafeNativeMethods.NavigateDirection.Parent:
                        return _owningGridViewListBox.AccessibilityObject;
                    case UnsafeNativeMethods.NavigateDirection.NextSibling:
                        int currentIndex = GetCurrentIndex();
                        var gridViewListBoxAccessibleObject = _owningGridViewListBox.AccessibilityObject as GridViewListBoxAccessibleObject;
                        if (gridViewListBoxAccessibleObject != null) {
                            int itemsCount = gridViewListBoxAccessibleObject.GetChildFragmentCount();
                            int nextItemIndex = currentIndex + 1;
                            if (itemsCount > nextItemIndex) {
                                return gridViewListBoxAccessibleObject.GetChildFragment(nextItemIndex);
                            }
                        }
                        break;
                    case UnsafeNativeMethods.NavigateDirection.PreviousSibling:
                        currentIndex = GetCurrentIndex();
                        gridViewListBoxAccessibleObject = _owningGridViewListBox.AccessibilityObject as GridViewListBoxAccessibleObject;
                        if (gridViewListBoxAccessibleObject != null) {
                            var itemsCount = gridViewListBoxAccessibleObject.GetChildFragmentCount();
                            int previousItemIndex = currentIndex - 1;
                            if (previousItemIndex >= 0) {
                                return gridViewListBoxAccessibleObject.GetChildFragment(previousItemIndex);
                            }
                        }
                        
                        break;
                }
 
                return base.FragmentNavigate(direction);
            }
 
            internal override UnsafeNativeMethods.IRawElementProviderFragmentRoot FragmentRoot {
                get {
                    return _owningGridViewListBox.AccessibilityObject;
                }
            }
 
            private int GetCurrentIndex() {
                return _owningGridViewListBox.Items.IndexOf(_owningItem);
            }
            
            internal override int GetChildId() {
                return GetCurrentIndex() + 1; // Index is zero-based, Child ID is 1-based.
            }
 
            internal override object GetPropertyValue(int propertyID) {
                switch (propertyID) {
                    case NativeMethods.UIA_RuntimeIdPropertyId:
                        return RuntimeId;
                    case NativeMethods.UIA_BoundingRectanglePropertyId:
                        return BoundingRectangle;
                    case NativeMethods.UIA_ControlTypePropertyId:
                        return NativeMethods.UIA_ListItemControlTypeId;
                    case NativeMethods.UIA_NamePropertyId:
                        return Name;
                    case NativeMethods.UIA_AccessKeyPropertyId:
                        return KeyboardShortcut;
                    case NativeMethods.UIA_HasKeyboardFocusPropertyId:
                        return _owningGridViewListBox.Focused;
                    case NativeMethods.UIA_IsKeyboardFocusablePropertyId:
                        return (State & AccessibleStates.Focusable) == AccessibleStates.Focusable;
                    case NativeMethods.UIA_IsEnabledPropertyId:
                        return _owningGridViewListBox.Enabled;
                    case NativeMethods.UIA_HelpTextPropertyId:
                        return Help ?? string.Empty;
                    case NativeMethods.UIA_IsPasswordPropertyId:
                        return false;
                    case NativeMethods.UIA_IsOffscreenPropertyId:
                        return (State & AccessibleStates.Offscreen) == AccessibleStates.Offscreen;
                    default:
                        return base.GetPropertyValue(propertyID);
                }
            }
 
            /// <summary>
            /// Gets the help text.
            /// </summary>
            public override string Help {
                get {
                    var systemIAccessible = GetSystemIAccessibleInternal();
                    return systemIAccessible.accHelp[GetChildId()];
                }
            }
 
            /// <summary>
            /// Gets the keyboard shortcut.
            /// </summary>
            public override string KeyboardShortcut {
                get {
                    var systemIAccessible = GetSystemIAccessibleInternal();
                    return systemIAccessible.get_accKeyboardShortcut(GetChildId());
                }
            }
 
            /// <summary>
            /// Indicates whether specified pattern is supported.
            /// </summary>
            /// <param name="patternId">The pattern ID.</param>
            /// <returns>True if specified </returns>
            internal override bool IsPatternSupported(int patternId) {
                if (patternId == NativeMethods.UIA_LegacyIAccessiblePatternId ||
                    patternId == NativeMethods.UIA_InvokePatternId) {
                    return true;
                }
 
                return base.IsPatternSupported(patternId);
            }
            
            /// <summary>
            /// Gets or sets the accessible name.
            /// </summary>
            public override string Name {
                get {
                    if (_owningGridViewListBox != null) {
                        return _owningItem.ToString();
                    }
 
                    return base.Name;
                }
 
                set {
                    base.Name = value;
                }
            }
 
            /// <summary>
            /// Gets the accessible role.
            /// </summary>
            public override AccessibleRole Role {
                get {
                    var systemIAccessible = GetSystemIAccessibleInternal();
                    return (AccessibleRole)systemIAccessible.get_accRole(GetChildId());
                }
            }
            
            /// <summary>
            /// Gets the runtime ID.
            /// </summary>
            internal override int[] RuntimeId {
                get {
                    var runtimeId = new int[3];
                    runtimeId[0] = RuntimeIDFirstItem;
                    runtimeId[1] = (int)(long)_owningGridViewListBox.Handle;
                    runtimeId[2] = _owningItem.GetHashCode();
 
                    return runtimeId;
                }
            }
 
            /// <summary>
            /// Gets the accessible state.
            /// </summary>
            public override AccessibleStates State {
                get {
                    var systemIAccessible = GetSystemIAccessibleInternal();
                    return (AccessibleStates)systemIAccessible.get_accState(GetChildId());
                }
            }
 
            internal override void SetFocus() {
                RaiseAutomationEvent(NativeMethods.UIA_AutomationFocusChangedEventId);
 
                base.SetFocus();
            }
        }
 
        private class GridViewListBoxItemAccessibleObjectCollection : Hashtable {
 
            private GridViewListBox _owningGridViewListBox;
 
            public GridViewListBoxItemAccessibleObjectCollection(GridViewListBox owningGridViewListBox) {
                _owningGridViewListBox = owningGridViewListBox;
            }
 
            public override object this[object key] {
                get {
                    if (!ContainsKey(key)) {
                        var itemAccessibleObject = new GridViewListBoxItemAccessibleObject(_owningGridViewListBox, key);
                        base[key] = itemAccessibleObject;
                    }
 
                    return base[key];
                }
 
                set {
                    base[key] = value;
                }
            }
        }
 
        /// <summary>
        /// Represents the PropertyGridView ListBox accessibility object.
        /// </summary>
        [ComVisible(true)]
        private class GridViewListBoxAccessibleObject : ControlAccessibleObject {
 
            private GridViewListBox _owningGridViewListBox;
            private PropertyGridView _owningPropertyGridView;
            private GridViewListBoxItemAccessibleObjectCollection _itemAccessibleObjects;
 
            /// <summary>
            /// Constructs the new instance of GridViewListBoxAccessibleObject.
            /// </summary>
            /// <param name="owningGridViewListBox">The owning GridViewListBox.</param>
            public GridViewListBoxAccessibleObject(GridViewListBox owningGridViewListBox) : base(owningGridViewListBox) {
                _owningGridViewListBox = owningGridViewListBox;
                _owningPropertyGridView = owningGridViewListBox.OwningPropertyGridView;
                _itemAccessibleObjects = new GridViewListBoxItemAccessibleObjectCollection(owningGridViewListBox);
            }
 
            /// <summary>
            /// Request to return the element in the specified direction.
            /// </summary>
            /// <param name="direction">Indicates the direction in which to navigate.</param>
            /// <returns>Returns the element in the specified direction.</returns>
            internal override UnsafeNativeMethods.IRawElementProviderFragment FragmentNavigate(UnsafeNativeMethods.NavigateDirection direction) {
                if (direction == UnsafeNativeMethods.NavigateDirection.Parent) {
                    return _owningPropertyGridView.SelectedGridEntry.AccessibilityObject;
                }
                else if (direction == UnsafeNativeMethods.NavigateDirection.FirstChild) {
                    return GetChildFragment(0);
                }
                else if (direction == UnsafeNativeMethods.NavigateDirection.LastChild) {
                    var childFragmentCount = GetChildFragmentCount();
                    if (childFragmentCount > 0) {
                        return GetChildFragment(childFragmentCount - 1);
                    }
                }
                else if (direction == UnsafeNativeMethods.NavigateDirection.NextSibling) {
                    return _owningPropertyGridView.Edit.AccessibilityObject;
                }
 
                return base.FragmentNavigate(direction);
            }
 
            /// <summary>
            /// Return the element that is the root node of this fragment of UI.
            /// </summary>
            internal override UnsafeNativeMethods.IRawElementProviderFragmentRoot FragmentRoot {
                get {
                    return _owningPropertyGridView.AccessibilityObject;
                }
            }
 
            public AccessibleObject GetChildFragment(int index) {
                if (index < 0  || index >= _owningGridViewListBox.Items.Count) {
                    return null;
                }
 
                var item = _owningGridViewListBox.Items[index];
                return _itemAccessibleObjects[item] as AccessibleObject;
            }
 
 
            public int GetChildFragmentCount() {
                return _owningGridViewListBox.Items.Count;
            }
 
            /// <summary>
            /// Request value of specified property from an element.
            /// </summary>
            /// <param name="propertyId">Identifier indicating the property to return</param>
            /// <returns>Returns a ValInfo indicating whether the element supports this property, or has no value for it.</returns>
            internal override object GetPropertyValue(int propertyID) {
                if (propertyID == NativeMethods.UIA_ControlTypePropertyId) {
                    return NativeMethods.UIA_ListControlTypeId;
                } else if (propertyID == NativeMethods.UIA_NamePropertyId) {
                    return Name;
                }
 
                return base.GetPropertyValue(propertyID);
            }
 
            internal override void SetFocus() {
                RaiseAutomationEvent(NativeMethods.UIA_AutomationFocusChangedEventId);
 
                base.SetFocus();
            }
 
            internal void SetListBoxItemFocus() {
                var selectedItem = _owningGridViewListBox.SelectedItem;
                var itemAccessibleObject = _itemAccessibleObjects[selectedItem] as AccessibleObject;
                if (itemAccessibleObject != null) {
                    itemAccessibleObject.SetFocus();
                }
            }
        }
 
        private class GridViewEdit : TextBox , IMouseHookClient {
 
            internal bool fInSetText = false;
            internal bool filter = false;
            internal PropertyGridView psheet = null;
            private  bool dontFocusMe = false;
            private int   lastMove;
            
            private MouseHook mouseHook;
            
            // We do this becuase the Focus call above doesn't always stick, so
            // we make the Edit think that it doesn't have focus.  this prevents
            // ActiveControl code on the containercontrol from moving focus elsewhere
            // when the dropdown closes.
            public bool DontFocus {
               set {
                  dontFocusMe = value;
               }
            }
                        
            public virtual bool Filter {
                get { return filter;}
 
                set {
                    this.filter = value;
                }
            }
 
            /// <summary>
            /// Indicates whether or not the control supports UIA Providers via
            /// IRawElementProviderFragment/IRawElementProviderFragmentRoot interfaces
            /// </summary>
            internal override bool SupportsUiaProviders {
                get {
                    return AccessibilityImprovements.Level3;
                }
            }
 
            public override bool Focused {
                get {
                    if (dontFocusMe) {
                        return false;
                    }
                    return base.Focused;
                }
            }
 
 
            public override string Text {
                get {
                    return base.Text;
                }
                set {
                    fInSetText = true;
                    base.Text = value;
                    fInSetText = false;
                }
            }
            
            public bool DisableMouseHook {
               
                set {
                    mouseHook.DisableMouseHook = value;
                }
            }
 
 
            public virtual bool HookMouseDown{
                get{
                    return mouseHook.HookMouseDown;
                }
                set{
                    mouseHook.HookMouseDown = value;
                    if (value) {
                        this.FocusInternal();
                    }
                }
            }
 
 
            public GridViewEdit(PropertyGridView psheet) {
                this.psheet = psheet;
                mouseHook = new MouseHook(this, this, psheet);
            }
        
            /// <summary>
            /// Creates a new AccessibleObject for this GridViewEdit instance.
            /// The AccessibleObject instance returned by this method overrides several UIA properties.
            /// However the new object is only available in applications that are recompiled to target 
            /// .NET Framework 4.7.2 or opt in into this feature using a compatibility switch. 
            /// </summary>
            /// <returns>
            /// AccessibleObject for this GridViewEdit instance.
            /// </returns>
            protected override AccessibleObject CreateAccessibilityInstance() {
                if (AccessibilityImprovements.Level2) {
                    return new GridViewEditAccessibleObject(this);
                }
 
                return base.CreateAccessibilityInstance();
            }
 
            protected override void DestroyHandle() {
                  mouseHook.HookMouseDown = false;
                  base.DestroyHandle();
            }
 
            protected override void Dispose(bool disposing) {
                if (disposing) {
                    mouseHook.Dispose();
                }
                base.Dispose(disposing);
            }
 
            public void FilterKeyPress(char keyChar) {
            
                if (IsInputChar(keyChar)) {
                    this.FocusInternal();
                    this.SelectAll();
                    UnsafeNativeMethods.PostMessage(new HandleRef(this, Handle), NativeMethods.WM_CHAR, (IntPtr)keyChar, IntPtr.Zero);
                }
            }
    
    
 
            /// <include file='doc\PropertyGridView.uex' path='docs/doc[@for="PropertyGridView.GridViewEdit.IsInputKey"]/*' />
            /// <devdoc>
            ///     Overridden to handle TAB key.
            /// </devdoc>
            protected override bool IsInputKey(Keys keyData) {
                switch (keyData & Keys.KeyCode) {
                    case Keys.Escape:
                    case Keys.Tab:
                    case Keys.F4:
                    case Keys.F1:
                    case Keys.Return:
                        return false;
                }
                if (psheet.NeedsCommit) {
                    return false;
                }
                return base.IsInputKey(keyData);
            }
 
            /// <include file='doc\PropertyGridView.uex' path='docs/doc[@for="PropertyGridView.GridViewEdit.IsInputChar"]/*' />
            /// <devdoc>
            ///     Overridden to handle TAB key.
            /// </devdoc>
            protected override bool IsInputChar(char keyChar) {
                switch ((Keys)(int)keyChar) {
                    case Keys.Tab:
                    case Keys.Return:
                        return false;
                }
                return base.IsInputChar(keyChar);
            }
 
            protected override void OnKeyDown(KeyEventArgs ke) {
 
                // this is because on a dialog we may
                // not get a chance to pre-process
                //
                if (ProcessDialogKey(ke.KeyData)) {
                    ke.Handled = true;
                    return;
                }
 
                base.OnKeyDown(ke);
            }
 
            protected override void OnKeyPress(KeyPressEventArgs ke) {
                if (!IsInputChar(ke.KeyChar)) {
                    ke.Handled = true;
                    return;
                }
                base.OnKeyPress(ke);
            }
            
            public bool OnClickHooked() {
                 // can we commit this value?
                 // eat the value if we failed to commit.
                 return !psheet._Commit();
            }
 
            protected override void OnMouseEnter(EventArgs e) {
               base.OnMouseEnter(e);
               
               if (!this.Focused) {
                  Graphics g = CreateGraphics();
                  if (psheet.SelectedGridEntry != null &&
                      ClientRectangle.Width <= psheet.SelectedGridEntry.GetValueTextWidth(this.Text, g, this.Font)) {
                      psheet.ToolTip.ToolTip = this.PasswordProtect ? "" : this.Text;
                  }
                  g.Dispose();
               }
               
            }
 
            protected override bool ProcessCmdKey(ref Message msg, Keys keyData) {
            
                 // make sure we allow the Edit to handle ctrl-z
                 switch (keyData & Keys.KeyCode) {
                     case Keys.Z:
                     case Keys.C:
                     case Keys.X:
                     case Keys.V:
                        if(
                           ((keyData & Keys.Control) != 0) && 
                           ((keyData & Keys.Shift) == 0) &&
                           ((keyData & Keys.Alt) == 0)) {
                           return false;
                        }
                        break;
                        
                     case Keys.A:
                        if(
                           ((keyData & Keys.Control) != 0) && 
                           ((keyData & Keys.Shift) == 0) &&
                           ((keyData & Keys.Alt) == 0)) {
                           SelectAll();
                           return true;
                        }
                        
                        break;
                        
                     case Keys.Insert:
                         if (((keyData & Keys.Alt) == 0)) {
                             if (((keyData & Keys.Control) != 0) ^ ((keyData & Keys.Shift) == 0)) {
                                 return false;
                             }
                         }
                         break;
                         
                     case Keys.Delete:
                         if(
                           ((keyData & Keys.Control) == 0) && 
                           ((keyData & Keys.Shift) != 0) &&
                           ((keyData & Keys.Alt) == 0)) {
                           return false;
                         }
                         else if(
                               ((keyData & Keys.Control) == 0) && 
                               ((keyData & Keys.Shift) == 0) &&
                               ((keyData & Keys.Alt) == 0)
                                 )
                         {
                             // if this is just the delete key and we're on a non-text editable property that is resettable,
                             // reset it now.
                             //
                             if (psheet.SelectedGridEntry != null && !psheet.SelectedGridEntry.Enumerable && !psheet.SelectedGridEntry.IsTextEditable && psheet.SelectedGridEntry.CanResetPropertyValue()) {
                                 object oldValue = psheet.SelectedGridEntry.PropertyValue;
                                 psheet.SelectedGridEntry.ResetPropertyValue();
                                 psheet.UnfocusSelection();
                                 psheet.ownerGrid.OnPropertyValueSet(psheet.SelectedGridEntry, oldValue);
                             }
                         }
                         break;
                 }
                 return base.ProcessCmdKey(ref msg, keyData);
            }
 
 
            /// <include file='doc\PropertyGridView.uex' path='docs/doc[@for="PropertyGridView.GridViewEdit.ProcessDialogKey"]/*' />
            /// <devdoc>
            ///      Overrides Control.ProcessDialogKey to handle the Escape and Return
            ///      keys.
            /// </devdoc>
            /// <internalonly/>
            protected override bool ProcessDialogKey(Keys keyData) {
 
                // We don't do anything with modified keys here.
                //
                if ((keyData & (Keys.Shift | Keys.Control | Keys.Alt)) == 0) {
                    switch (keyData & Keys.KeyCode) {
                        case Keys.Return: 
                            bool fwdReturn = !psheet.NeedsCommit;
                            if (psheet.UnfocusSelection() && fwdReturn) {
                              psheet.SelectedGridEntry.OnValueReturnKey();
                            }
                            return true;
                        case Keys.Escape:
                            psheet.OnEscape(this);
                            return true;
                        case Keys.F4:
                            psheet.F4Selection(true);
                            return true;
                    }
                }
                
                // for the tab key, we want to commit before we allow it to be processed.
                if ((keyData & Keys.KeyCode) == Keys.Tab && ((keyData & (Keys.Control | Keys.Alt)) == 0)) {
                    return !psheet._Commit();
                }
 
                return base.ProcessDialogKey(keyData);
            }
 
            protected override void SetVisibleCore(bool value) {
                Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose,  "DropDownHolder:Visible(" + (value.ToString()) + ")");
                // make sure we dont' have the mouse captured if
                // we're going invisible
                if (value == false && this.HookMouseDown) {
                    mouseHook.HookMouseDown = false;
                }
                base.SetVisibleCore(value);
            }
 
            
            // a mini version of process dialog key
            // for responding to WM_GETDLGCODE
            internal bool WantsTab(bool forward) {
                return psheet.WantsTab(forward);
            }
      
            private unsafe bool WmNotify(ref Message m) {
                
                if (m.LParam != IntPtr.Zero) {
                   NativeMethods.NMHDR* nmhdr = (NativeMethods.NMHDR*)m.LParam;
                   
                   if (nmhdr->hwndFrom == psheet.ToolTip.Handle) {
                       switch (nmhdr->code) {
                          case NativeMethods.TTN_SHOW:
                             psheet.ToolTip.PositionToolTip(this, ClientRectangle);
                             m.Result = (IntPtr)1;
                             return true;
                          default:
                             psheet.WndProc(ref m);
                             break;
                       }         
                   }
                }
                return false;
            }
            
            protected override void WndProc(ref Message m) {
 
                if (filter) {
                    if (psheet.FilterEditWndProc(ref m)) {
                        return;
                    }
                }
 
                switch (m.Msg) {
                    case NativeMethods.WM_STYLECHANGED:
                        if ((unchecked( (int) (long)m.WParam) & NativeMethods.GWL_EXSTYLE) != 0) {
                            psheet.Invalidate();
                        }
                        break;
                    case NativeMethods.WM_MOUSEMOVE:
                        if (unchecked( (int) (long)m.LParam) == lastMove) {
                            return;
                        }
                        lastMove = unchecked( (int) (long)m.LParam);
                        break;
                    case NativeMethods.WM_DESTROY:
                        mouseHook.HookMouseDown = false;
                        break;
                    case NativeMethods.WM_SHOWWINDOW:
                        if (IntPtr.Zero == m.WParam) {
                            mouseHook.HookMouseDown = false;
                        }
                        break;
                    case NativeMethods.WM_PASTE:
                        /*if (!this.ReadOnly) {
                            IDataObject dataObject = Clipboard.GetDataObject();
                            Debug.Assert(dataObject != null, "Failed to get dataObject from clipboard");
                            if (dataObject != null) {
                                object data = dataObject.GetData(typeof(string));
                                if (data != null) {
                                    string clipboardText = data.ToString();
                                    SelectedText = clipboardText;
                                    m.result = 1;
                                    return;
                                }
                            }
                        }*/
                        if (this.ReadOnly) {
                           return;
                        }
                        break;
                                                            
                    case NativeMethods.WM_GETDLGCODE:
 
                        m.Result = (IntPtr)((long)m.Result | NativeMethods.DLGC_WANTARROWS | NativeMethods.DLGC_WANTCHARS);
                        if (psheet.NeedsCommit || this.WantsTab((ModifierKeys & Keys.Shift) == 0)) {
                            m.Result = (IntPtr)((long)m.Result | NativeMethods.DLGC_WANTALLKEYS | NativeMethods.DLGC_WANTTAB);
                        }
                        return;
                        
                    case NativeMethods.WM_NOTIFY:
                        if (WmNotify(ref m))
                            return;
                        break;                                                              
                }
                base.WndProc(ref m);
            }
 
            public virtual bool InSetText() {
                return fInSetText;
            }
 
            [ComVisible(true)]
            protected class GridViewEditAccessibleObject : ControlAccessibleObject {
 
                private PropertyGridView propertyGridView;
 
                public GridViewEditAccessibleObject(GridViewEdit owner) : base(owner) {
                    this.propertyGridView = owner.psheet;
                }
 
                public override AccessibleStates State {
                    get {
                        AccessibleStates states = base.State;
                        if (this.IsReadOnly) {
                            states |= AccessibleStates.ReadOnly;
                        }
                        else {
                            states &= ~AccessibleStates.ReadOnly;
                        }
                        return states;
                    }
                }
 
                internal override bool IsIAccessibleExSupported() {
                    return true;
                }
 
                /// <summary>
                /// Returns the element in the specified direction.
                /// </summary>
                /// <param name="direction">Indicates the direction in which to navigate.</param>
                /// <returns>Returns the element in the specified direction.</returns>
                internal override UnsafeNativeMethods.IRawElementProviderFragment FragmentNavigate(UnsafeNativeMethods.NavigateDirection direction) {
                    if (AccessibilityImprovements.Level3) {
                        if (direction == UnsafeNativeMethods.NavigateDirection.Parent) {
                            return propertyGridView.SelectedGridEntry.AccessibilityObject;
                        }
                        else if (direction == UnsafeNativeMethods.NavigateDirection.NextSibling) {
                            if (propertyGridView.DropDownButton.Visible) {
                                return propertyGridView.DropDownButton.AccessibilityObject;
                            } else if (propertyGridView.DialogButton.Visible) {
                                return propertyGridView.DialogButton.AccessibilityObject;
                            }
                        }
                    }
 
                    return base.FragmentNavigate(direction);
                }
 
                /// <summary>
                /// Gets the top level element.
                /// </summary>
                internal override UnsafeNativeMethods.IRawElementProviderFragmentRoot FragmentRoot {
                    get {
                        if (AccessibilityImprovements.Level3) {
                            return propertyGridView.AccessibilityObject;
                        }
 
                        return base.FragmentRoot;
                    }
                }
 
                internal override object GetPropertyValue(int propertyID) {
                    if (propertyID == NativeMethods.UIA_IsEnabledPropertyId) {
                        return !this.IsReadOnly;
                    }
                    else if (propertyID == NativeMethods.UIA_IsValuePatternAvailablePropertyId) {
                        return IsPatternSupported(NativeMethods.UIA_ValuePatternId);
                    }
 
                    if (AccessibilityImprovements.Level3) {
                        if (propertyID == NativeMethods.UIA_ControlTypePropertyId) {
                            return NativeMethods.UIA_EditControlTypeId;
                        }
                        else if (propertyID == NativeMethods.UIA_NamePropertyId) {
                            return Name;
                        }
                    }
 
                    return base.GetPropertyValue(propertyID);
                }
 
                internal override bool IsPatternSupported(int patternId) {
                    if (patternId == NativeMethods.UIA_ValuePatternId) {
                        return true;
                    }
 
                    return base.IsPatternSupported(patternId);
                }
 
                public override string Name {
                    get {
                        if (AccessibilityImprovements.Level3) {
                            string name = Owner.AccessibleName;
                            if (name != null) {
                                return name;
                            }
                            else {
                                var selectedGridEntry = propertyGridView.SelectedGridEntry;
                                if (selectedGridEntry != null) {
                                    return selectedGridEntry.AccessibilityObject.Name;
                                }
                            }
                        }
 
                        return base.Name;
                    }
 
                    set {
                        base.Name = value;
                    }
                }
 
                #region IValueProvider
 
                internal override bool IsReadOnly {
                    get {
                        PropertyDescriptorGridEntry propertyDescriptorGridEntry = this.propertyGridView.SelectedGridEntry as PropertyDescriptorGridEntry;
                        return propertyDescriptorGridEntry == null || propertyDescriptorGridEntry.IsPropertyReadOnly;
                    }
                }
 
                #endregion
 
                internal override void SetFocus() {
                    if (AccessibilityImprovements.Level3) {
                        RaiseAutomationEvent(NativeMethods.UIA_AutomationFocusChangedEventId);
                    }
 
                    base.SetFocus();
                }
            }
        }
        
        internal interface IMouseHookClient {
        
            // return true if the click is handled, false
            // to pass it on
            bool OnClickHooked();        
        }                                             
        
        internal class MouseHook {
            private PropertyGridView gridView;
            private Control          control;
            private IMouseHookClient client;
            
            internal int        thisProcessID = 0;
            private GCHandle    mouseHookRoot;
            private IntPtr      mouseHookHandle = IntPtr.Zero;
            private bool        hookDisable = false;
            
            private bool processing;      
            
            public MouseHook(Control control, IMouseHookClient client, PropertyGridView gridView) {
               this.control = control;
               this.gridView = gridView;
               this.client = client;
               #if DEBUG
               callingStack = Environment.StackTrace;
               #endif
            }       
 
            #if DEBUG
            string callingStack;
            ~MouseHook() {
                Debug.Assert(mouseHookHandle == IntPtr.Zero, "Finalizing an active mouse hook.  This will crash the process.  Calling stack: " + callingStack);
            }
            #endif
            
            
            public bool DisableMouseHook {
           
                set {
                    hookDisable = value;
                    if (value) {
                        UnhookMouse();
                    }
                }
            }
 
 
            public virtual bool HookMouseDown{
                get{   
                    GC.KeepAlive(this);
                    return mouseHookHandle != IntPtr.Zero;
                }
                set{
                    if (value && !hookDisable) {
                        HookMouse();
                    }
                    else {
                        UnhookMouse();
                    }
                }
            }
 
            
            public void Dispose() {
               UnhookMouse();
            }  
                    
 
            /// <include file='doc\PropertyGridView.uex' path='docs/doc[@for="PropertyGridView.MouseHook.HookMouse"]/*' />
            /// <devdoc>
            ///     Sets up the needed windows hooks to catch messages.
            /// </devdoc>
            /// <internalonly/>
            [ResourceExposure(ResourceScope.Process)]
            [ResourceConsumption(ResourceScope.Process)]
            private void HookMouse() {
                GC.KeepAlive(this);
                // Locking 'this' here is ok since this is an internal class.  See VSW#464499.
                lock(this) {
                    if (mouseHookHandle != IntPtr.Zero) {
                        return;
                    }
                    
                    if (thisProcessID == 0) {
                        SafeNativeMethods.GetWindowThreadProcessId(new HandleRef(control, control.Handle), out thisProcessID);
                    }
                  
                    
                    NativeMethods.HookProc hook = new NativeMethods.HookProc(new MouseHookObject(this).Callback);
                    mouseHookRoot = GCHandle.Alloc(hook);
 
                    mouseHookHandle = UnsafeNativeMethods.SetWindowsHookEx(NativeMethods.WH_MOUSE,
                                                               hook,
                                                               NativeMethods.NullHandleRef,
                                                               SafeNativeMethods.GetCurrentThreadId());
                    Debug.Assert(mouseHookHandle != IntPtr.Zero, "Failed to install mouse hook");
                    Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose,  "DropDownHolder:HookMouse()");
                }
            }
            
            /// <include file='doc\PropertyGridView.uex' path='docs/doc[@for="PropertyGridView.MouseHook.MouseHookProc"]/*' />
            /// <devdoc>
            ///     HookProc used for catch mouse messages.
            /// </devdoc>
            /// <internalonly/>
            private IntPtr MouseHookProc(int nCode, IntPtr wparam, IntPtr lparam) {
                GC.KeepAlive(this);
                if (nCode == NativeMethods.HC_ACTION) {
                    NativeMethods.MOUSEHOOKSTRUCT mhs = (NativeMethods.MOUSEHOOKSTRUCT)UnsafeNativeMethods.PtrToStructure(lparam, typeof(NativeMethods.MOUSEHOOKSTRUCT));
                    if (mhs != null) {
                        switch (unchecked( (int) (long)wparam)) {
                            case NativeMethods.WM_LBUTTONDOWN:
                            case NativeMethods.WM_MBUTTONDOWN:
                            case NativeMethods.WM_RBUTTONDOWN:
                            case NativeMethods.WM_NCLBUTTONDOWN:
                            case NativeMethods.WM_NCMBUTTONDOWN:
                            case NativeMethods.WM_NCRBUTTONDOWN:
                            case NativeMethods.WM_MOUSEACTIVATE:
                                if (ProcessMouseDown(mhs.hWnd, mhs.pt_x, mhs.pt_y)) {
                                    return (IntPtr)1;
                                }
                                break;
                        }
 
                    }
                }
 
                return UnsafeNativeMethods.CallNextHookEx(new HandleRef(this, mouseHookHandle), nCode, wparam, lparam);
            }
            
            /// <include file='doc\PropertyGridView.uex' path='docs/doc[@for="PropertyGridView.MouseHook.UnhookMouse"]/*' />
            /// <devdoc>
            ///     Removes the windowshook that was installed.
            /// </devdoc>
            /// <internalonly/>
            private void UnhookMouse() {
                GC.KeepAlive(this);
                // Locking 'this' here is ok since this is an internal class.  See VSW#464499.
                lock(this) {
                    if (mouseHookHandle != IntPtr.Zero) {
                        UnsafeNativeMethods.UnhookWindowsHookEx(new HandleRef(this, mouseHookHandle));
                        mouseHookRoot.Free();
                        mouseHookHandle = IntPtr.Zero;
                        Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose,  "DropDownHolder:UnhookMouse()");
                    }
                }
            }
           
             /*
            * Here is where we force validation on any clicks outside the
            */
            private bool ProcessMouseDown(IntPtr hWnd, int x, int y) {
            
               
               // com+ 12678
               // if we put up the "invalid" message box, it appears this 
               // method is getting called re-entrantly when it shouldn't be.
               // this prevents us from recursing.
               //
               if (processing) {
                  return false;
               }
               
                IntPtr hWndAtPoint = hWnd;
                IntPtr handle = control.Handle;
                Control ctrlAtPoint = Control.FromHandleInternal(hWndAtPoint);
 
                // if it's us or one of our children, just process as normal
                //
                if (hWndAtPoint != handle && !control.Contains(ctrlAtPoint)) {
                    Debug.Assert(thisProcessID != 0, "Didn't get our process id!");
 
                    // make sure the window is in our process
                    int pid;
                    SafeNativeMethods.GetWindowThreadProcessId(new HandleRef(null, hWndAtPoint), out pid);
 
                    // if this isn't our process, unhook the mouse.
                    if (pid != thisProcessID) {
                        this.HookMouseDown = false;
                        return false;
                    }
 
                    bool needCommit = false;   
 
                    // if this a sibling control (e.g. the drop down or buttons), just forward the message and skip the commit
                    needCommit = ctrlAtPoint == null ?  true : !gridView.IsSiblingControl(control, ctrlAtPoint);
                    
                    try {
                       processing = true;
                       
                       if (needCommit) {
                           if (client.OnClickHooked()) {
                               return true; // there was an error, so eat the mouse
                           }
                           /* This breaks all sorts of stuff.  Need to find a better way to do this but we can't figure
                              out the scenario this addressed.
                           else {
                               // Returning false lets the message go to its destination.  Only
                               // return false if there is still a mouse button down.  That might not be the
                               // case if committing the entry opened a modal dialog.
                               //
                               MouseButtons state = Control.MouseButtons;
                               return (int)state == 0;
                           }
                           */
                       }
                    }
                    finally {
                       processing = false;
                    }
 
                    // cancel our hook at this point
                    HookMouseDown = false;
                    //gridView.UnfocusSelection();
                }
                return false;
            }
            
              /// <include file='doc\PropertyGridView.uex' path='docs/doc[@for="PropertyGridView.MouseHook.MouseHookObject"]/*' />
              /// <devdoc>
            ///     Forwards messageHook calls to ToolTip.messageHookProc
            /// </devdoc>
            /// <internalonly/>
            private class MouseHookObject {
                internal WeakReference reference;
 
                public MouseHookObject(MouseHook parent) {
                    this.reference = new WeakReference(parent, false);
                }
 
                public virtual IntPtr Callback(int nCode, IntPtr wparam, IntPtr lparam) {
                    IntPtr ret = IntPtr.Zero;
                    try {
                        MouseHook control = (MouseHook)reference.Target;
                        if (control != null) {
                            ret = control.MouseHookProc(nCode, wparam, lparam);
                        }
                    }
                    catch {
                        // ignore
                    }
                    return ret;
                }
            }
        }
 
        /// <include file='doc\PropertyGridView.uex' path='docs/doc[@for="PropertyGridView.PropertyGridViewAccessibleObject"]/*' />
        /// <devdoc>
        ///     The accessible object class for a PropertyGridView. The child accessible objects
        ///     are accessible objects corresponding to the property grid entries.        
        /// </devdoc>
        [ComVisible(true)]
        internal class PropertyGridViewAccessibleObject : ControlAccessibleObject {
 
            private PropertyGridView _owningPropertyGridView;
            private PropertyGrid _parentPropertyGrid;
 
            /// <include file='doc\PropertyGridView.uex' path='docs/doc[@for="PropertyGridView.PropertyGridViewAccessibleObject.PropertyGridViewAccessibleObject"]/*' />
            /// <devdoc>
            ///     Construct a PropertyGridViewAccessibleObject
            /// </devdoc>
            public PropertyGridViewAccessibleObject(PropertyGridView owner, PropertyGrid parentPropertyGrid) : base(owner) {
                _owningPropertyGridView = owner;
                _parentPropertyGrid = parentPropertyGrid;
            }
 
            /// <summary>
            /// Return the child element at the specified point, if one exists,
            /// otherwise return this element if the point is on this element,
            /// otherwise return null.
            /// </summary>
            /// <param name="x">x coordinate of point to check</param>
            /// <param name="y">y coordinate of point to check</param>
            /// <returns>Return the child element at the specified point, if one exists,
            /// otherwise return this element if the point is on this element,
            /// otherwise return null.
            /// </returns>
            internal override UnsafeNativeMethods.IRawElementProviderFragment ElementProviderFromPoint(double x, double y) {
                if (AccessibilityImprovements.Level3) {
                    return HitTest((int)x, (int)y);
                }
 
                return base.ElementProviderFromPoint(x, y);
            }
 
            /// <summary>
            /// Request to return the element in the specified direction.
            /// </summary>
            /// <param name="direction">Indicates the direction in which to navigate.</param>
            /// <returns>Returns the element in the specified direction.</returns>
            internal override UnsafeNativeMethods.IRawElementProviderFragment FragmentNavigate(UnsafeNativeMethods.NavigateDirection direction) {
                if (AccessibilityImprovements.Level3) {
                    var propertyGridAccessibleObject = _parentPropertyGrid.AccessibilityObject as PropertyGridAccessibleObject;
                    if (propertyGridAccessibleObject != null) {
                        var navigationTarget = propertyGridAccessibleObject.ChildFragmentNavigate(this, direction);
                        if (navigationTarget != null) {
                            return navigationTarget;
                        }
                    }
 
                    if (_owningPropertyGridView.OwnerGrid.SortedByCategories) {
                        switch (direction) {
                            case UnsafeNativeMethods.NavigateDirection.FirstChild:
                                return GetFirstCategory();
                            case UnsafeNativeMethods.NavigateDirection.LastChild:
                                return GetLastCategory();
                        }
                    }
                    else {
                        switch (direction) {
                            case UnsafeNativeMethods.NavigateDirection.FirstChild:
                                return GetChild(0);
                            case UnsafeNativeMethods.NavigateDirection.LastChild:
                                int childCount = GetChildCount();
                                if (childCount > 0) {
                                    return GetChild(childCount - 1);
                                }
 
                                return null;
                        }
                    }
                }
 
                return base.FragmentNavigate(direction);
            }
 
            /// <summary>
            /// Return the element that is the root node of this fragment of UI.
            /// </summary>
            internal override UnsafeNativeMethods.IRawElementProviderFragmentRoot FragmentRoot {
                get {
                    if (AccessibilityImprovements.Level3) {
                        return _owningPropertyGridView.OwnerGrid.AccessibilityObject;
                    }
 
                    return base.FragmentRoot;
                }
            }
 
            /// <summary>
            /// Gets the accessible object for the currently focused grid entry.
            /// </summary>
            /// <returns>The accessible object for the currently focused grid entry.</returns>
            internal override UnsafeNativeMethods.IRawElementProviderFragment GetFocus() {
                if (AccessibilityImprovements.Level3) {
                    return GetFocused();
                }
 
                return base.FragmentRoot;
            }
 
            /// <summary>
            /// Request value of specified property from an element.
            /// </summary>
            /// <param name="propertyId">Identifier indicating the property to return</param>
            /// <returns>Returns a ValInfo indicating whether the element supports this property, or has no value for it.</returns>
            internal override object GetPropertyValue(int propertyID) {
                if (AccessibilityImprovements.Level3) {
                    if (propertyID == NativeMethods.UIA_ControlTypePropertyId) {
                        return NativeMethods.UIA_TableControlTypeId;
                    } else if (propertyID == NativeMethods.UIA_NamePropertyId) {
                        return Name;
                    }
                }
 
                return base.GetPropertyValue(propertyID);
            }
 
            public override string Name {
                get {
                    string name = Owner.AccessibleName;
                    if (name != null) {
                        return name;
                    }
                    else {
                        return SR.GetString(SR.PropertyGridDefaultAccessibleName);
                    }
                }
            }
            
            public override AccessibleRole Role {
                get {
                    AccessibleRole role = Owner.AccessibleRole;
                    if (role != AccessibleRole.Default) {
                        return role;
                    }
                    else {
                        return AccessibleRole.Table;
                    }
                }
            }                                                         
 
            public AccessibleObject Next(GridEntry current) {
                int row = ((PropertyGridView)Owner).GetRowFromGridEntry(current);
                GridEntry nextEntry = ((PropertyGridView)Owner).GetGridEntryFromRow(++row);
                if (nextEntry != null) {
                    return nextEntry.AccessibilityObject;
                }
                return null;
            }
 
            internal AccessibleObject GetCategory(int categoryIndex) {
                GridEntry[] targetEntries = new GridEntry[1];
                var topLevelGridEntries = _owningPropertyGridView.TopLevelGridEntries;
                var topLevelGridEntriesCount = topLevelGridEntries.Count;
                if (topLevelGridEntriesCount > 0) {
                    var targetEntry = topLevelGridEntries[categoryIndex];
                    var categoryGridEntry = targetEntry as CategoryGridEntry;
                    if (categoryGridEntry != null) {
                        return categoryGridEntry.AccessibilityObject;
                    }
                }
 
                return null;
            }
 
            internal AccessibleObject GetFirstCategory() {
                return GetCategory(0);
            }
 
            internal AccessibleObject GetLastCategory() {
                var topLevelGridEntries = _owningPropertyGridView.TopLevelGridEntries;
                var topLevelGridEntriesCount = topLevelGridEntries.Count;
                return GetCategory(topLevelGridEntries.Count - 1);
            }
 
            /// <summary>
            /// Gets the previous grid entry accessibility object.
            /// </summary>
            /// <param name="currentGridEntry">The current grid entry.</param>
            /// <param name="gridEntryCollection">The grid entry collection.</param>
            /// <param name="currentGridEntryFound">Indicates whether the current grid entry is found.</param>
            /// <returns>The previous grid entry.</returns>
            internal AccessibleObject GetPreviousGridEntry(GridEntry currentGridEntry, GridEntryCollection gridEntryCollection, out bool currentGridEntryFound) {
                GridEntry previousGridEntry = null;
                currentGridEntryFound = false;
 
                foreach (GridEntry gridEntry in gridEntryCollection) {
                    if (currentGridEntry == gridEntry) {
                        // Set to true to return the previous iterable element.
                        currentGridEntryFound = true;
                        if (previousGridEntry != null) {
                            // In the current iteration return previous entry if the current entry == iterated grid entry.
                            return previousGridEntry.AccessibilityObject;
                        }
                        else {
                            return null;
                        }
                    }
                    else {
                        previousGridEntry = gridEntry;
                        if (gridEntry.ChildCount > 0) {
                            var foundChild = GetPreviousGridEntry(currentGridEntry, gridEntry.Children, out currentGridEntryFound);
                            if (foundChild != null) {
                                // Return some down-level child if found.
                                return foundChild;
                            }
                            else if (currentGridEntryFound) {
                                // If the passed current is found but there is no next near this current.
                                return null;
                            }
                        }
                    }
                }
 
                return null;
            }
 
            /// <summary>
            /// Gets the next grid entry.
            /// </summary>
            /// <param name="currentGridEntry">The current grid entry.</param>
            /// <param name="gridEntryCollection">The grid entry collection.</param>
            /// <param name="currentGridEntryFound">Indicates whether the current grid entry is found.</param>
            /// <returns>The next grid entry.</returns>
            internal AccessibleObject GetNextGridEntry(GridEntry currentGridEntry, GridEntryCollection gridEntryCollection, out bool currentGridEntryFound) {
                currentGridEntryFound = false;
 
                foreach (GridEntry gridEntry in gridEntryCollection) {
                    if (currentGridEntryFound) {
                        // Return the next entry via IEnumerable.Next() if previous entry == passed current.
                        return gridEntry.AccessibilityObject;
                    }
 
                    if (currentGridEntry == gridEntry) {
                        // Set to true to return the next iterable element. (see above)
                        currentGridEntryFound = true;
                    }
                    else if (gridEntry.ChildCount > 0) {
                        var foundChild = GetNextGridEntry(currentGridEntry, gridEntry.Children, out currentGridEntryFound);
                        if (foundChild != null) {
                            // Return some down-level child if found.
                            return foundChild;
                        }
                        else if (currentGridEntryFound) {
                            // If the passed current is found but there is no next near this current.
                            return null;
                        }
                    }
                }
 
                return null;
            }
 
            /// <summary>
            /// Gets the first child property.
            /// </summary>
            /// <param name="current">The current grid entry.</param>
            /// <returns>The first child property.</returns>
            internal AccessibleObject GetFirstChildProperty(CategoryGridEntry current) {
                if (current.ChildCount > 0) {
                    GridEntryCollection subGridEntry = current.Children;
                    if (subGridEntry != null && subGridEntry.Count > 0) {
                        GridEntry[] targetEntries = new GridEntry[1];
                        try {
                            _owningPropertyGridView.GetGridEntriesFromOutline(subGridEntry, 0, 0, targetEntries);
                        }
                        catch (Exception ex) {
                            Debug.Fail(ex.ToString());
                        }
 
                        return targetEntries[0].AccessibilityObject;
                    }
                }
 
                return null;
            }
 
            /// <summary>
            /// Gets the last child property.
            /// </summary>
            /// <param name="current">The current grid entry.</param>
            /// <returns>The last child property.</returns>
            internal AccessibleObject GetLastChildProperty(CategoryGridEntry current) {
                if (current.ChildCount > 0) {
                    GridEntryCollection subGridEntry = current.Children;
                    if (subGridEntry != null && subGridEntry.Count > 0) {
                        GridEntry[] targetEntries = new GridEntry[1];
                        try {
                            _owningPropertyGridView.GetGridEntriesFromOutline(subGridEntry, 0, subGridEntry.Count - 1, targetEntries);
                        }
                        catch (Exception ex) {
                            Debug.Fail(ex.ToString());
                        }
 
                        return targetEntries[0].AccessibilityObject;
                    }
                }
 
                return null;
            }
 
            /// <summary>
            /// Gets the next category.
            /// </summary>
            /// <param name="current">The current grid entry.</param>
            /// <returns>The next category.</returns>
            internal AccessibleObject GetNextCategory(CategoryGridEntry current) {
                int row = _owningPropertyGridView.GetRowFromGridEntry(current);
 
                GridEntry nextEntry;
 
                do {
                    nextEntry = _owningPropertyGridView.GetGridEntryFromRow(++row);
                    if (nextEntry is CategoryGridEntry) {
                        return nextEntry.AccessibilityObject;
                    }
                }
                while (nextEntry != null);
 
                return null;
            }
 
            public AccessibleObject Previous(GridEntry current) {
                int row = ((PropertyGridView)Owner).GetRowFromGridEntry(current);
                GridEntry prevEntry = ((PropertyGridView)Owner).GetGridEntryFromRow(--row);
                if (prevEntry != null) {
                    return prevEntry.AccessibilityObject;
                }
                return null;
            }
 
            /// <summary>
            /// Gets the previous category.
            /// </summary>
            /// <param name="current">The current grid entry.</param>
            /// <returns>The previous category.</returns>
            internal AccessibleObject GetPreviousCategory(CategoryGridEntry current) {
                int row = _owningPropertyGridView.GetRowFromGridEntry(current);
 
                GridEntry previousEntry;
 
                do {
                    previousEntry = _owningPropertyGridView.GetGridEntryFromRow(--row);
                    if (previousEntry is CategoryGridEntry) {
                        return previousEntry.AccessibilityObject;
                    }
                }
                while (previousEntry != null);
 
                return null;
            }
 
            /// <include file='doc\PropertyGridView.uex' path='docs/doc[@for="PropertyGridView.PropertyGridViewAccessibleObject.GetChild"]/*' />
            /// <devdoc>
            ///      Get the accessible child at the given index.
            ///      The accessible children of a PropertyGridView are accessible objects
            ///      corresponding to the property grid entries.
            /// </devdoc>
            public override AccessibleObject GetChild(int index) {
 
                GridEntryCollection properties = ((PropertyGridView)Owner).AccessibilityGetGridEntries();
                if (properties != null && index >= 0 && index < properties.Count) {
                    return properties.GetEntry(index).AccessibilityObject;
                }
                else {
                    return null;
                }
            }
 
            /// <include file='doc\PropertyGridView.uex' path='docs/doc[@for="PropertyGridView.PropertyGridViewAccessibleObject.GetChildCount"]/*' />
            /// <devdoc>
            ///      Get the number of accessible children.
            ///      The accessible children of a PropertyGridView are accessible objects
            ///      corresponding to the property grid entries.
            /// </devdoc>
            public override int GetChildCount() {
                GridEntryCollection properties = ((PropertyGridView)Owner).AccessibilityGetGridEntries();
 
                if (properties != null) {
                    return properties.Count;
                }
                else {
                    return 0;
                }
            }
 
            /// <include file='doc\PropertyGridView.uex' path='docs/doc[@for="PropertyGridView.PropertyGridViewAccessibleObject.GetFocused"]/*' />
            /// <devdoc>
            ///      Get the accessible object for the currently focused grid entry.
            /// </devdoc>
            public override AccessibleObject GetFocused() {
            
                GridEntry gridEntry = ((PropertyGridView)Owner).SelectedGridEntry;
                if (gridEntry != null && gridEntry.Focus) {
                    return gridEntry.AccessibilityObject;
                }
                return null;
            }
 
            /// <include file='doc\PropertyGridView.uex' path='docs/doc[@for="PropertyGridView.PropertyGridViewAccessibleObject.GetSelected"]/*' />
            /// <devdoc>
            ///      Get the accessible object for the currently selected grid entry.
            /// </devdoc>
            public override AccessibleObject GetSelected() {
                GridEntry gridEntry = ((PropertyGridView)Owner).SelectedGridEntry;
                if (gridEntry != null) {
                    return gridEntry.AccessibilityObject;
                }
                return null;
            }
 
 
 
            /// <include file='doc\PropertyGridView.uex' path='docs/doc[@for="PropertyGridView.PropertyGridViewAccessibleObject.HitTest"]/*' />
            /// <devdoc>
            ///      Get the accessible child at the given screen location.
            ///      The accessible children of a PropertyGridView are accessible objects
            ///      corresponding to the property grid entries.
            /// </devdoc>
            public override AccessibleObject HitTest(int x, int y) {
 
                // Convert to client coordinates
                //
                NativeMethods.POINT pt = new NativeMethods.POINT(x, y);
                UnsafeNativeMethods.ScreenToClient(new HandleRef(Owner, Owner.Handle), pt);
 
                // Find the grid entry at the given client coordinates
                //
                Point pos = ((PropertyGridView)Owner).FindPosition(pt.x, pt.y);
                if (pos != PropertyGridView.InvalidPosition) {
                    GridEntry gridEntry = ((PropertyGridView)Owner).GetGridEntryFromRow(pos.Y);
                    if (gridEntry != null) {
 
                        // Return the accessible object for this grid entry
                        //
                        return gridEntry.AccessibilityObject;
                    }
                }
 
                // No grid entry at this point
                //
                return null;
            }
 
            /// <include file='doc\PropertyGridView.uex' path='docs/doc[@for="PropertyGridView.PropertyGridViewAccessibleObject.Navigate"]/*' />
            /// <devdoc>
            ///      Navigate to another object.
            /// </devdoc>
            [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)]
            public override AccessibleObject Navigate(AccessibleNavigation navdir) {
 
                if (GetChildCount() > 0) {
                    // We're only handling FirstChild and LastChild here
                    switch(navdir) {
                        case AccessibleNavigation.FirstChild:
                            return GetChild(0);
                        case AccessibleNavigation.LastChild:
                            return GetChild(GetChildCount() - 1);
                    }
                }
                return null;    // Perform default behavior
            }
        }
 
        internal class GridPositionData {
 
            ArrayList expandedState;
            GridEntryCollection selectedItemTree;
            int       itemRow;
            int       itemCount;
 
            public GridPositionData(PropertyGridView gridView) {
                selectedItemTree = gridView.GetGridEntryHierarchy(gridView.selectedGridEntry);
                expandedState = gridView.SaveHierarchyState(gridView.topLevelGridEntries);
                itemRow = gridView.selectedRow;
                itemCount = gridView.totalProps;
            }
 
            public GridEntry Restore(PropertyGridView gridView) {
                    gridView.RestoreHierarchyState(expandedState);
                    GridEntry entry = gridView.FindEquivalentGridEntry(selectedItemTree);
 
                    if (entry != null) {
                        gridView.SelectGridEntry(entry, true);
                        
                        int delta = gridView.selectedRow - itemRow;
                        if (delta != 0 && gridView.ScrollBar.Visible) {
                            if (itemRow < gridView.visibleRows) {
                                delta += gridView.GetScrollOffset();
                                
                                if (delta < 0) {
                                    delta = 0;
                                }
                                else if (delta > gridView.ScrollBar.Maximum) {
                                    delta = gridView.ScrollBar.Maximum - 1;
                                }
                                gridView.SetScrollOffset(delta);
                            }
                            
                        }
                    }
                    return entry;
            }
        }
    }
}