File: winforms\Managed\System\WinForms\PropertyGridInternal\GridEntry.cs
Project: ndp\fx\src\System.Windows.Forms.csproj (System.Windows.Forms)
//------------------------------------------------------------------------------
// <copyright file="GridEntry.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>                                                                
//------------------------------------------------------------------------------
 
//#define PBRS_PAINT_DEBUG
/*
 */
namespace System.Windows.Forms.PropertyGridInternal {
    using System.Security.Permissions;
    using System.Runtime.Serialization.Formatters;
    using System.Runtime.Remoting;
    using System.Runtime.InteropServices;
    using System.ComponentModel;
    using System.Diagnostics;
    using System;
    using System.Collections;
    using System.Reflection;
    using System.Globalization;
 
    using System.Drawing.Design;    
    using System.ComponentModel.Design;
    using System.Windows.Forms;
    using System.Windows.Forms.Design;
    using System.Windows.Forms.Internal;
    using System.Windows.Forms.VisualStyles;
    using System.Drawing;
    using System.Drawing.Drawing2D;
    using Microsoft.Win32;
 
    /// <include file='doc\GridEntry.uex' path='docs/doc[@for="GridEntry"]/*' />
    /// <devdoc>
    ///     Base Entry for properties to be displayed in properties window.
    /// </devdoc>
    internal abstract class GridEntry : GridItem, ITypeDescriptorContext {
 
        protected static readonly Point InvalidPoint = new Point(int.MinValue, int.MinValue);
        private static BooleanSwitch PbrsAssertPropsSwitch = new BooleanSwitch("PbrsAssertProps", "PropertyBrowser : Assert on broken properties");
 
        internal static AttributeTypeSorter AttributeTypeSorter = new AttributeTypeSorter();
        
        // Type flags
        internal const int FLAG_TEXT_EDITABLE             = 0x0001;
        internal const int FLAG_ENUMERABLE                = 0x0002;
        internal const int FLAG_CUSTOM_PAINT              = 0x0004;
        internal const int FLAG_IMMEDIATELY_EDITABLE      = 0x0008;
        internal const int FLAG_CUSTOM_EDITABLE           = 0x0010;
        internal const int FLAG_DROPDOWN_EDITABLE         = 0x0020;
        internal const int FLAG_LABEL_BOLD                = 0x0040;
        internal const int FLAG_READONLY_EDITABLE         = 0x0080;
        internal const int FLAG_RENDER_READONLY           = 0x0100;
        internal const int FLAG_IMMUTABLE                 = 0x0200;
        internal const int FLAG_FORCE_READONLY            = 0x0400;
        internal const int FLAG_RENDER_PASSWORD           = 0x1000;
 
        internal const int FLAG_DISPOSED                  = 0x2000;
 
        internal const int FL_EXPAND                   = 0x00010000;
        internal const int FL_EXPANDABLE               = 0x00020000;
        //protected const int FL_EXPANDABLE_VALID         = 0x00040000;
        internal const int FL_EXPANDABLE_FAILED        = 0x00080000;
        internal const int FL_NO_CUSTOM_PAINT          = 0x00100000;
        internal const int FL_CATEGORIES               = 0x00200000;
        internal const int FL_CHECKED                  = unchecked((int)0x80000000);
 
        // rest are GridEntry constants.
 
        protected const int NOTIFY_RESET                = 1;
        protected const int NOTIFY_CAN_RESET            = 2;
        protected const int NOTIFY_DBL_CLICK            = 3;
        protected const int NOTIFY_SHOULD_PERSIST       = 4;
        protected const int NOTIFY_RETURN               = 5;
 
        protected const int     OUTLINE_ICON_PADDING    = 5;
        
        protected static IComparer DisplayNameComparer    = new DisplayNameSortComparer();
        
        private static char passwordReplaceChar;
        //Maximum number of characters we'll show in the property grid.  Too many characters leads
        //to bad performance.
        private const int maximumLengthOfPropertyString = 1000;
 
        [Flags]
        internal enum PaintValueFlags{
            None = 0,
            DrawSelected = 0x1,
            FetchValue   = 0x2,
            CheckShouldSerialize = 0x4,
            PaintInPlace = 0x8
        }
 
        private class CacheItems {
            public string lastLabel;
            public Font   lastLabelFont;
            public int    lastLabelWidth;
            public string lastValueString;
            public Font   lastValueFont;
            public int    lastValueTextWidth;
            public object lastValue;
            public bool   useValueString;
            public bool   lastShouldSerialize;
            public bool   useShouldSerialize;
            public bool   useCompatTextRendering;
        }
 
        private CacheItems cacheItems;
 
 
        // instance variables.
        protected TypeConverter     converter     = null;
        protected UITypeEditor      editor        = null;
        internal  GridEntry         parentPE      = null;
        private   GridEntryCollection childCollection = null;
        internal  int               flags         = 0;
        private   int               propertyDepth = 0;
        protected bool              hasFocus      = false;
        private   Rectangle         outlineRect   = Rectangle.Empty;
        protected PropertySort      PropertySort;
 
        protected Point             labelTipPoint = InvalidPoint;
        protected Point             valueTipPoint = InvalidPoint;
 
        protected PropertyGrid      ownerGrid;
 
        private   static object      EVENT_VALUE_CLICK = new object();
        private   static object      EVENT_LABEL_CLICK = new object();
        private   static object      EVENT_OUTLINE_CLICK = new object();
        private   static object      EVENT_VALUE_DBLCLICK = new object();
        private   static object      EVENT_LABEL_DBLCLICK = new object();
        private   static object      EVENT_OUTLINE_DBLCLICK = new object();
        private   static object      EVENT_RECREATE_CHILDREN = new object();
 
        private GridEntryAccessibleObject accessibleObject = null;
 
        private   bool               lastPaintWithExplorerStyle = false;
 
        private static Color InvertColor(Color color) {
            return Color.FromArgb(color.A, (byte)~color.R, (byte)~color.G, (byte)~color.B);
        }
 
        protected GridEntry(PropertyGrid owner, GridEntry peParent) {
            parentPE = peParent;
            ownerGrid = owner;
 
            Debug.Assert( this.ownerGrid != null, "GridEntry w/o PropertyGrid owner, text rendering will fail." );
 
            if (peParent != null) {
                propertyDepth = peParent.PropertyDepth + 1;
                this.PropertySort = peParent.PropertySort;
 
                if (peParent.ForceReadOnly) {
                    flags |= FLAG_FORCE_READONLY;
                }
 
            }
            else {
                propertyDepth = -1;
            }
        }
 
        /// <summary>
        /// Outline Icon padding
        /// </summary>
        private int OutlineIconPadding {
            get {
                if (DpiHelper.EnableDpiChangedHighDpiImprovements) {
                    if (this.GridEntryHost != null) {
                        return this.GridEntryHost.LogicalToDeviceUnits(OUTLINE_ICON_PADDING);
                    }
                }
 
                return OUTLINE_ICON_PADDING;
            }
        }
 
        private bool colorInversionNeededInHC {
            get {
                 return SystemInformation.HighContrast && !OwnerGrid.developerOverride && AccessibilityImprovements.Level1;
            }
        }
 
        public AccessibleObject AccessibilityObject {
 
            get {
                if (accessibleObject == null) {
                    accessibleObject = GetAccessibilityObject();
                }
                return accessibleObject;
            }
        }
 
        protected virtual GridEntryAccessibleObject GetAccessibilityObject() {
            return new GridEntryAccessibleObject(this);
        }
 
        /// <include file='doc\GridEntry.uex' path='docs/doc[@for="GridEntry.AllowMerge"]/*' />
        /// <devdoc>
        /// specify that this grid entry should be allowed to be merged for.
        /// multi-select.
        /// </devdoc>
        public virtual bool AllowMerge {
            get {
                return true;
            }
        }
        
        internal virtual bool AlwaysAllowExpand {
            get {
               return false;
            }
        }
 
        internal virtual AttributeCollection Attributes {
            get {
                return TypeDescriptor.GetAttributes(PropertyType);
            }
        }
 
        /// <include file='doc\GridEntry.uex' path='docs/doc[@for="GridEntry.BackgroundBrush"]/*' />
        /// <devdoc>
        /// Gets the value of the background brush to use.  Override
        /// this member to cause the entry to paint it's background in a different color.
        /// The base implementation returns null.
        /// </devdoc>
        protected virtual Brush GetBackgroundBrush(Graphics g) {
            return GridEntryHost.GetBackgroundBrush(g);
        }
 
        /// <include file='doc\GridEntry.uex' path='docs/doc[@for="GridEntry.LabelTextColor"]/*' />
        /// <devdoc>
        /// </devdoc>
        protected virtual Color LabelTextColor {
            get {
                if (this.ShouldRenderReadOnly) {
                    return GridEntryHost.GrayTextColor;
                }
                else {
                    return GridEntryHost.GetTextColor();
                }
            }
        }
 
        /// <include file='doc\GridEntry.uex' path='docs/doc[@for="GridEntry.BrowsableAttributes"]/*' />
        /// <devdoc>
        /// The set of attributes that will be used for browse filtering
        /// </devdoc>
        public virtual AttributeCollection BrowsableAttributes {
            get{
                if (parentPE != null) {
                    return parentPE.BrowsableAttributes;
                }
                return null;
            }
            set{
                parentPE.BrowsableAttributes = value;
            }
        }
 
        /// <include file='doc\GridEntry.uex' path='docs/doc[@for="GridEntry.Component"]/*' />
        /// <devdoc>
        ///      Retrieves the component that is invoking the
        ///      method on the formatter object.  This may
        ///      return null if there is no component
        ///      responsible for the call.
        /// </devdoc>
        public virtual IComponent Component {
            get {
                object owner = GetValueOwner();
                if (owner is IComponent) {
                    return(IComponent) owner;
                }
                if (parentPE != null) {
                    return parentPE.Component;
                }
                return null;
            }
        }
 
        protected virtual IComponentChangeService ComponentChangeService {
            get {
                return parentPE.ComponentChangeService;
            }
 
        }
 
        /// <include file='doc\GridEntry.uex' path='docs/doc[@for="GridEntry.Container"]/*' />
        /// <devdoc>
        ///      Retrieves the container that contains the
        ///      set of objects this formatter may work
        ///      with.  It may return null if there is no
        ///      container, or of the formatter should not
        ///      use any outside objects.
        /// </devdoc>
        public virtual IContainer Container {
            get {
                IComponent component = Component;
                if (component != null) {
                    ISite site = component.Site;
                    if (site != null) {
                        return site.Container;
                    }
                }
                return null;
            }
        }
 
        protected GridEntryCollection ChildCollection{
            get {
                if (childCollection == null) {
                    childCollection = new GridEntryCollection(this, null);
                }
                return childCollection;
            }
            set {
                Debug.Assert(value == null || !Disposed, "Why are we putting new children in after we are disposed?");
                if (this.childCollection != value) {
                    if (this.childCollection != null) {
                        this.childCollection.Dispose();
                        this.childCollection = null;
                    }
                    this.childCollection = value;
                }
            }
        }
 
        public int ChildCount {
            get {
                if (Children != null) {
                    return Children.Count;
                }
                return 0;
            }
        }
 
        public virtual GridEntryCollection Children {
            get {
                if (childCollection == null && !Disposed) {
                    CreateChildren();
                }
                return childCollection;
            }
        }
 
        public virtual PropertyTab CurrentTab{
            get{
                if (parentPE != null) {
                    return parentPE.CurrentTab;
                }
                return null;
            }
            set{
                if (parentPE != null) {
                    parentPE.CurrentTab = value;
                }
            }
        }
 
        /// <include file='doc\GridEntry.uex' path='docs/doc[@for="GridEntry.DefaultChild"]/*' />
        /// <devdoc>
        /// Returns the default child GridEntry of this item.  Usually the default property
        /// of the target object.
        /// </devdoc>
        internal virtual GridEntry DefaultChild {
            get {
                return null;
            }
            set{}
        }
 
        internal virtual IDesignerHost DesignerHost{
            get{
                if (parentPE != null) {
                    return parentPE.DesignerHost;
                }
                return null;
            }
            set {
                if (parentPE != null) {
                    parentPE.DesignerHost = value;
                }
            }
        }
 
        internal bool Disposed{
            get {
                return GetFlagSet(FLAG_DISPOSED);
            }
        }
 
        internal virtual bool Enumerable {
            get {
                return(this.Flags & GridEntry.FLAG_ENUMERABLE) != 0;
            }
        }
 
 
        public override bool Expandable {
            get {
                bool fExpandable = GetFlagSet(FL_EXPANDABLE);
 
                if (fExpandable && childCollection != null && childCollection.Count > 0) {
                    return true;
                }
 
                if (GetFlagSet(FL_EXPANDABLE_FAILED)) {
                    return false;
                }
 
                if (fExpandable && (cacheItems == null || cacheItems.lastValue == null) && this.PropertyValue == null) {
                    fExpandable = false;
                }
 
                return fExpandable;
            }
        }
 
        public override bool Expanded {
            get{
                return InternalExpanded;
            }
            set {
                GridEntryHost.SetExpand(this, value);
            }
        }
 
        internal virtual bool ForceReadOnly {
            get {
                return (flags & FLAG_FORCE_READONLY) != 0;
            }
        }
        
         internal virtual bool InternalExpanded {
            get{
                // short circuit if we don't have children
                if (childCollection == null || childCollection.Count == 0) {
                    return false;
                }
                return GetFlagSet(FL_EXPAND);
            }
            set {
                if (!this.Expandable || value == this.InternalExpanded) {
                    return;
                }
 
                if (childCollection != null && childCollection.Count > 0) {
                    SetFlag(FL_EXPAND,value);
                }
                else {
                    SetFlag(FL_EXPAND,false);
                    if (value) {
                        bool fMakeSure = CreateChildren();
                        SetFlag(FL_EXPAND,fMakeSure);
                    }
                }
 
                if (AccessibilityImprovements.Level1) {
                    // Notify accessibility clients of expanded state change
                    // StateChange requires NameChange, too - accessible clients won't see this, unless both events are raised
 
                    // Root item is hidden and should not raise events
                    if (GridItemType != GridItemType.Root) {
                        int id = ((PropertyGridView)GridEntryHost).AccessibilityGetGridEntryChildID(this);
                        if (id >= 0) {
                            PropertyGridView.PropertyGridViewAccessibleObject gridAccObj =
                                (PropertyGridView.PropertyGridViewAccessibleObject)((PropertyGridView)GridEntryHost).AccessibilityObject;
 
                            gridAccObj.NotifyClients(AccessibleEvents.StateChange, id);
                            gridAccObj.NotifyClients(AccessibleEvents.NameChange, id);
                        }
                    }
                }
            }
        }
 
        internal virtual int Flags {
            get {
                if ((flags & FL_CHECKED) != 0) {
                    return flags;
                }
 
                flags |= FL_CHECKED;
 
                TypeConverter converter = TypeConverter;
                UITypeEditor  uiEditor  = UITypeEditor;
                object value = Instance;
                bool forceReadOnly = this.ForceReadOnly;
 
                if (value != null) {
                     forceReadOnly |= TypeDescriptor.GetAttributes(value).Contains(InheritanceAttribute.InheritedReadOnly);
                }
                
                if (converter.GetStandardValuesSupported(this)) {
                    flags |= GridEntry.FLAG_ENUMERABLE;
                }
 
                if (!forceReadOnly && converter.CanConvertFrom(this, typeof(string)) &&
                    !converter.GetStandardValuesExclusive(this)) {
                    flags |= GridEntry.FLAG_TEXT_EDITABLE;
                }
                
                bool isImmutableReadOnly = TypeDescriptor.GetAttributes(this.PropertyType)[typeof(ImmutableObjectAttribute)].Equals(ImmutableObjectAttribute.Yes);
                bool isImmutable = isImmutableReadOnly || converter.GetCreateInstanceSupported(this);
                
                if (isImmutable) {
                    flags |= GridEntry.FLAG_IMMUTABLE;
                }
 
                if (converter.GetPropertiesSupported(this)) {
                    flags |= GridEntry.FL_EXPANDABLE;
 
                    // If we're exapndable, but we don't support editing,
                    // make us read only editable so we don't paint grey.
                    //
                    if (!forceReadOnly && (flags & GridEntry.FLAG_TEXT_EDITABLE) == 0 && !isImmutableReadOnly) {
                        flags |= GridEntry.FLAG_READONLY_EDITABLE;
                    }
                }
 
                if (Attributes.Contains(PasswordPropertyTextAttribute.Yes)) {
                    flags |= GridEntry.FLAG_RENDER_PASSWORD;
                }
 
                if (uiEditor != null) {
                    if (uiEditor.GetPaintValueSupported(this)) {
                        flags |= GridEntry.FLAG_CUSTOM_PAINT;
                    }
 
                    // We only allow drop-downs if the object is NOT being inherited
                    // I would really rather this not be here, but we have other places where
                    // we make read-only properties editable if they have drop downs.  Not
                    // sure this is the right thing...is it?
 
                    bool allowButtons = !forceReadOnly;
                    
                    if (allowButtons) {
                        switch (uiEditor.GetEditStyle(this)) {
                            case UITypeEditorEditStyle.Modal:
                                flags |= GridEntry.FLAG_CUSTOM_EDITABLE;
                                if (!isImmutable && !PropertyType.IsValueType) {
                                    flags |= GridEntry.FLAG_READONLY_EDITABLE;
                                }
                                break;
                            case UITypeEditorEditStyle.DropDown:
                                flags |= GridEntry.FLAG_DROPDOWN_EDITABLE;
                                break;
                        }
                    }
                }
 
                return flags;
 
            }
            set {
                flags = value;
            }
        }
 
        /// <include file='doc\GridEntry.uex' path='docs/doc[@for="GridEntry.Focus"]/*' />
        /// <devdoc>
        /// Checks if the entry is currently expanded
        /// </devdoc>
        public bool Focus {
            get{
                return this.hasFocus;
            }
            set{
 
                if (Disposed) {
                    return;
                }
 
                if (cacheItems != null) {
                    cacheItems.lastValueString = null;
                    cacheItems.useValueString = false;
                    cacheItems.useShouldSerialize = false;
                }
                
                if (this.hasFocus != value) {
                    this.hasFocus = value;
 
                    // Notify accessibility applications that keyboard focus has changed.
                    //
                    if (value == true) {
                        int id = ((PropertyGridView)GridEntryHost).AccessibilityGetGridEntryChildID(this);
                        if (id >= 0) {
                            PropertyGridView.PropertyGridViewAccessibleObject gridAccObj = 
                                (PropertyGridView.PropertyGridViewAccessibleObject)((PropertyGridView)GridEntryHost).AccessibilityObject;
                                
                            gridAccObj.NotifyClients(AccessibleEvents.Focus, id);
                            gridAccObj.NotifyClients(AccessibleEvents.Selection, id);
 
                            if (AccessibilityImprovements.Level3) {
                                AccessibilityObject.SetFocus();
                            }
                        }
                    }
                }
            }
        }
 
        /// <include file='doc\GridEntry.uex' path='docs/doc[@for="GridEntry.FullLabel"]/*' />
        /// <devdoc>
        /// Returns the label including the object name, and properties.  For example, the value
        /// of the Font size property on a Button called Button1 would be "Button1.Font.Size"
        /// </devdoc>
        public string FullLabel {
            get {
                string str = null;
                if (parentPE != null) {
                    str = parentPE.FullLabel;
                }
 
                if (str != null) {
                    str += ".";
                }
                else {
                    str = "";
                }
                str += this.PropertyLabel;
 
                return str;
            }
        }
        
        public override GridItemCollection GridItems {
            get {
                if (Disposed) {
                    throw new ObjectDisposedException(SR.GetString(SR.GridItemDisposed));
                }
                
                if (IsExpandable && childCollection != null && childCollection.Count == 0) {
                    CreateChildren();
                }
                
                return this.Children;
            }
        }
 
        internal virtual PropertyGridView GridEntryHost{
            get{        // ACCESSOR: virtual was missing from this get
                if (parentPE != null) {
                    return parentPE.GridEntryHost;
                }
                return null;
            }
            set {
                throw new NotSupportedException();
            }
        }
        
        public override GridItemType GridItemType {
            get {
                return GridItemType.Property;
            }
        }
 
        /// <include file='doc\GridEntry.uex' path='docs/doc[@for="GridEntry.HasValue"]/*' />
        /// <devdoc>
        /// Returns true if this GridEntry has a value field in the right hand column.
        /// </devdoc>
        internal virtual bool HasValue {
            get {
                return true;
            }
        }
 
        /// <include file='doc\GridEntry.uex' path='docs/doc[@for="GridEntry.HelpKeyword"]/*' />
        /// <devdoc>
        ///     Retrieves the keyword that the VS help dynamic help window will
        ///     use when this IPE is selected.
        /// </devdoc>
        public virtual string HelpKeyword {
            get {
                string keyWord = null;
 
                if (parentPE != null) {
                    keyWord = parentPE.HelpKeyword;
                }
                if (keyWord == null) {
                    keyWord = String.Empty;
                }
 
                return keyWord;
            }
        }
        
        internal virtual string HelpKeywordInternal{
            get {
               return this.HelpKeyword;
            }
        }
 
        public virtual bool IsCustomPaint {
            get {
                // prevent full flag population if possible.
                if ((flags & FL_CHECKED) == 0) {
                    UITypeEditor typeEd = this.UITypeEditor;
                    if (typeEd != null) {
                        if ((this.flags & GridEntry.FLAG_CUSTOM_PAINT) != 0 || 
                            (this.flags & GridEntry.FL_NO_CUSTOM_PAINT) != 0) {
                            return(this.flags & GridEntry.FLAG_CUSTOM_PAINT) != 0;
                        }
 
 
                        if (typeEd.GetPaintValueSupported(this)) {
                            flags |= GridEntry.FLAG_CUSTOM_PAINT;
                            return true;
                        }
                        else {
                            flags |= GridEntry.FL_NO_CUSTOM_PAINT;
                            return false;
                        }    
                    }
                }
                return(this.Flags & GridEntry.FLAG_CUSTOM_PAINT) != 0;
            }
        }
        
        public virtual bool IsExpandable {
            get {
                return this.Expandable;
            }
            set {
                if (value != GetFlagSet(FL_EXPANDABLE)) {
                    SetFlag(FL_EXPANDABLE_FAILED, false);
                    SetFlag(FL_EXPANDABLE, value);
                }
            }
        }
 
        public virtual bool IsTextEditable {
            get {
                return this.IsValueEditable && (this.Flags & GridEntry.FLAG_TEXT_EDITABLE) != 0;
            }
        }
 
        public virtual bool IsValueEditable {
            get {
                return !ForceReadOnly && 0 != (Flags & (GridEntry.FLAG_DROPDOWN_EDITABLE | GridEntry.FLAG_TEXT_EDITABLE | GridEntry.FLAG_CUSTOM_EDITABLE | GridEntry.FLAG_ENUMERABLE));
            }
        }
 
        /// <include file='doc\GridEntry.uex' path='docs/doc[@for="GridEntry.Instance"]/*' />
        /// <devdoc>
        ///      Retrieves the component that is invoking the
        ///      method on the formatter object.  This may
        ///      return null if there is no component
        ///      responsible for the call.
        /// </devdoc>
        public virtual object Instance {
            get {
                object owner = GetValueOwner();
 
                if (parentPE != null && owner == null) {
                    return parentPE.Instance;
                }
                return owner;
            }
        }
        
        public override string Label {
            get {
                return this.PropertyLabel;
            }
        }
        
        /// <include file='doc\GridEntry.uex' path='docs/doc[@for="GridEntry.PropertyDescriptor"]/*' />
        /// <devdoc>
        ///      Retrieves the PropertyDescriptor that is surfacing the given object/
        /// </devdoc>
        public override PropertyDescriptor PropertyDescriptor {
            get {
                return null;
            }
        }
 
        
 
        /// <include file='doc\GridEntry.uex' path='docs/doc[@for="GridEntry.PropertyLabelIndent"]/*' />
        /// <devdoc>
        /// Returns the pixel indent of the current GridEntry's label.
        /// </devdoc>
        internal virtual int PropertyLabelIndent {
            get {
                int borderWidth = this.GridEntryHost.GetOutlineIconSize() + OUTLINE_ICON_PADDING;
                return((propertyDepth + 1) * borderWidth) + 1;
            }
        }
 
        internal virtual Point GetLabelToolTipLocation(int mouseX, int mouseY) {
            return labelTipPoint;
        }
 
        internal virtual string LabelToolTipText {
            get {
                return this.PropertyLabel;
            }
        }
 
        public virtual bool NeedsDropDownButton{
            get {
                return(this.Flags & GridEntry.FLAG_DROPDOWN_EDITABLE) != 0;
            }
        }
 
        public virtual bool NeedsCustomEditorButton{
            get {
                return(this.Flags & GridEntry.FLAG_CUSTOM_EDITABLE) != 0 && (IsValueEditable || (Flags & GridEntry.FLAG_READONLY_EDITABLE) !=0);
            }
        }
 
        public PropertyGrid OwnerGrid{
            get{
                return this.ownerGrid;
            }
        }
 
        /// <include file='doc\GridEntry.uex' path='docs/doc[@for="GridEntry.OutlineRect"]/*' />
        /// <devdoc>
        /// Returns rect that the outline icon (+ or - or arrow) will be drawn into, relative
        /// to the upper left corner of the GridEntry.
        /// </devdoc>
        public Rectangle OutlineRect {
            get {
                if (!outlineRect.IsEmpty) {
                    return outlineRect;
                }
                PropertyGridView gridHost = this.GridEntryHost;
                Debug.Assert(gridHost != null, "No propEntryHost!");
                int outlineSize = gridHost.GetOutlineIconSize();
                int borderWidth = outlineSize + OutlineIconPadding;
                int left = (propertyDepth * borderWidth) + (OutlineIconPadding) / 2;
                int top = (gridHost.GetGridEntryHeight() - outlineSize) / 2;
                outlineRect = new Rectangle(left, top, outlineSize, outlineSize);
                return outlineRect;
            }
            set {
                // set property is required to reset cached value when dpi changed.
                if (value != outlineRect) {
                    outlineRect = value;
                }
            }
        }
 
        public virtual GridEntry ParentGridEntry{
            get {
                return this.parentPE;
            }
            set {
                Debug.Assert(value != this, "how can we be our own parent?");
                this.parentPE = value;
                if (value != null) {
                    propertyDepth = value.PropertyDepth+1;
 
                    // Microsoft, why do we do this?
                    if (this.childCollection != null) {
                        for (int i = 0; i < childCollection.Count; i++) {
                            childCollection.GetEntry(i).ParentGridEntry = this;
                        }
                    }
                }
            }
        }
        
        public override GridItem Parent {
            get {
                if (Disposed) {
                    throw new ObjectDisposedException(SR.GetString(SR.GridItemDisposed));
                }
                
                GridItem parent = this.ParentGridEntry;
                
                // don't allow walking all the way up to the parent.
                //
                //if (parent is IRootGridEntry) {
                //    return null;
                //}
                return parent;
            }
        }
 
        /// <include file='doc\GridEntry.uex' path='docs/doc[@for="GridEntry.PropertyCategory"]/*' />
        /// <devdoc>
        /// Returns category name of the current property
        /// </devdoc>
        public virtual string PropertyCategory {
            get {
                return CategoryAttribute.Default.Category;
            }
        }
 
        /// <include file='doc\GridEntry.uex' path='docs/doc[@for="GridEntry.PropertyDepth"]/*' />
        /// <devdoc>
        /// Returns "depth" of this property.  That is, how many parent's between
        /// this property and the root property.  The root property has a depth of -1.
        /// </devdoc>
        public virtual int PropertyDepth {
            get {
                return propertyDepth;
            }
        }
 
 
        /// <include file='doc\GridEntry.uex' path='docs/doc[@for="GridEntry.PropertyDescription"]/*' />
        /// <devdoc>
        /// Returns the description helpstring for this GridEntry.
        /// </devdoc>
        public virtual string PropertyDescription {
            get {
                return null;
            }
        }
 
        /// <include file='doc\GridEntry.uex' path='docs/doc[@for="GridEntry.PropertyLabel"]/*' />
        /// <devdoc>
        /// Returns the label of this property.  Usually
        /// this is the property name.
        /// </devdoc>
        public virtual string PropertyLabel {
            get {
                return null;
            }
        }
 
        /// <include file='doc\GridEntry.uex' path='docs/doc[@for="GridEntry.PropertyName"]/*' />
        /// <devdoc>
        /// Returns non-localized name of this property.
        /// </devdoc>
        public virtual string PropertyName {
            get {
                return this.PropertyLabel;
            }
        }
 
        /// <include file='doc\GridEntry.uex' path='docs/doc[@for="GridEntry.PropertyType"]/*' />
        /// <devdoc>
        /// Returns the Type of the value of this GridEntry, or null if the value is null.
        /// </devdoc>
        public virtual Type PropertyType {
            get {
                object obj = this.PropertyValue;
                if (obj != null) {
                    return obj.GetType();
                }
                else {
                    return null;
                }
            }
        }
 
        /// <include file='doc\GridEntry.uex' path='docs/doc[@for="GridEntry.PropertyValue"]/*' />
        /// <devdoc>
        /// Gets or sets the value for the property that is represented
        /// by this GridEntry.
        /// </devdoc>
        public virtual object PropertyValue{
            get {
                if (cacheItems != null) {
                    return cacheItems.lastValue;
                }
                return null;
            }
            set {
            }
        }
 
        public virtual bool ShouldRenderPassword {
            get {
                return (this.Flags & GridEntry.FLAG_RENDER_PASSWORD) != 0;
            }
        }
 
        public virtual bool ShouldRenderReadOnly {
            get {
                return ForceReadOnly || (0 != (this.Flags & GridEntry.FLAG_RENDER_READONLY) || (!this.IsValueEditable && (0 == (this.Flags & GridEntry.FLAG_READONLY_EDITABLE))));
            }
        }
 
        /// <include file='doc\GridEntry.uex' path='docs/doc[@for="GridEntry.TypeConverter"]/*' />
        /// <devdoc>
        /// Returns the type converter for this entry.
        /// </devdoc>
        internal virtual TypeConverter TypeConverter {
            get {
                if (converter == null) {
                    object value = this.PropertyValue;
                    if (value == null) {
                        converter = TypeDescriptor.GetConverter(this.PropertyType);
                    }
                    else {
                        converter = TypeDescriptor.GetConverter(value);
                    }
                }
                return converter;
            }
        }
 
        /// <include file='doc\GridEntry.uex' path='docs/doc[@for="GridEntry.UITypeEditor"]/*' />
        /// <devdoc>
        /// Returns the type editor for this entry.  This may return null if there
        /// is no type editor.
        /// </devdoc>
        internal virtual UITypeEditor UITypeEditor {
            get {
                if (editor == null && this.PropertyType != null) {
                    editor = (UITypeEditor)TypeDescriptor.GetEditor(this.PropertyType, typeof(System.Drawing.Design.UITypeEditor));
                }
 
                return editor;
            }
        }
        
        public override object Value {
            get {
                return this.PropertyValue;
            }
            // note: we don't do set because of the value class semantics, etc.
        }
 
        internal Point ValueToolTipLocation {
            get {
                return ShouldRenderPassword ? InvalidPoint : valueTipPoint;
            }
            set{
                valueTipPoint = value;
            }
        }
 
        internal int VisibleChildCount {
            get{
                if (!Expanded) {
                    return 0;
                }
                int count = ChildCount;
                int totalCount = count;
                for (int i = 0; i < count; i++) {
                     totalCount += ChildCollection.GetEntry(i).VisibleChildCount;
                }
                return totalCount;
            }
        }
 
 
        /// <include file='doc\GridEntry.uex' path='docs/doc[@for="GridEntry.AddOnLabelClick"]/*' />
        /// <devdoc>
        /// Add an event handler to be invoked when the label portion of
        /// the prop entry is clicked
        /// </devdoc>
        public virtual void AddOnLabelClick(EventHandler h) {
            AddEventHandler(EVENT_LABEL_CLICK, h);
        }
 
        /// <include file='doc\GridEntry.uex' path='docs/doc[@for="GridEntry.AddOnLabelDoubleClick"]/*' />
        /// <devdoc>
        /// Add an event handler to be invoked when the label portion of
        /// the prop entry is double
        /// </devdoc>
        public virtual void AddOnLabelDoubleClick(EventHandler h) {
            AddEventHandler(EVENT_LABEL_DBLCLICK, h);
        }
 
        /// <include file='doc\GridEntry.uex' path='docs/doc[@for="GridEntry.AddOnValueClick"]/*' />
        /// <devdoc>
        /// Add an event handler to be invoked when the value portion of
        /// the prop entry is clicked
        /// </devdoc>
        public virtual void AddOnValueClick(EventHandler h) {
            AddEventHandler(EVENT_VALUE_CLICK, h);
        }
 
 
        /// <include file='doc\GridEntry.uex' path='docs/doc[@for="GridEntry.AddOnValueDoubleClick"]/*' />
        /// <devdoc>
        /// Add an event handler to be invoked when the value portion of
        /// the prop entry is double-clicked
        /// </devdoc>
        public virtual void AddOnValueDoubleClick(EventHandler h) {
            AddEventHandler(EVENT_VALUE_DBLCLICK, h);
        }
 
        /// <include file='doc\GridEntry.uex' path='docs/doc[@for="GridEntry.AddOnOutlineClick"]/*' />
        /// <devdoc>
        /// Add an event handler to be invoked when the outline icone portion of
        /// the prop entry is clicked
        /// </devdoc>
        public virtual void AddOnOutlineClick(EventHandler h) {
            AddEventHandler(EVENT_OUTLINE_CLICK, h);
        }
 
        /// <include file='doc\GridEntry.uex' path='docs/doc[@for="GridEntry.AddOnOutlineDoubleClick"]/*' />
        /// <devdoc>
        /// Add an event handler to be invoked when the outline icone portion of
        /// the prop entry is double clicked
        /// </devdoc>
        public virtual void AddOnOutlineDoubleClick(EventHandler h) {
            AddEventHandler(EVENT_OUTLINE_DBLCLICK, h);
        }
 
        /// <include file='doc\GridEntry.uex' path='docs/doc[@for="GridEntry.AddOnRecreateChildren"]/*' />
        /// <devdoc>
        /// Add an event handler to be invoked when the children grid entries are re-created.
        /// </devdoc>
        public virtual void AddOnRecreateChildren(GridEntryRecreateChildrenEventHandler h) {
            AddEventHandler(EVENT_RECREATE_CHILDREN, h);
        }
 
        internal void ClearCachedValues() {
            ClearCachedValues(true);
        }
 
        internal void ClearCachedValues(bool clearChildren) {
               if (cacheItems != null) {
                  cacheItems.useValueString = false;
                  cacheItems.lastValue = null;
                  cacheItems.useShouldSerialize = false;
               }
               if (clearChildren) {
                   for (int i = 0; i < ChildCollection.Count; i++) {
                       ChildCollection.GetEntry(i).ClearCachedValues();
                   }
               }
        }
 
        /// <include file='doc\GridEntry.uex' path='docs/doc[@for="GridEntry.ConvertTextToValue"]/*' />
        /// <devdoc>
        /// Converts the given string of text to a value.
        /// </devdoc>
        public object ConvertTextToValue(string text) {
            if (TypeConverter.CanConvertFrom(this, typeof(string))) {
                return TypeConverter.ConvertFromString(this, text);
            }
            return text;
        }
 
        /// <include file='doc\GridEntry.uex' path='docs/doc[@for="GridEntry.Create"]/*' />
        /// <devdoc>
        /// Create the base prop entries given an object or set of objects
        /// </devdoc>
        internal static IRootGridEntry Create(PropertyGridView view, object[] rgobjs, IServiceProvider baseProvider, IDesignerHost currentHost, PropertyTab tab, PropertySort initialSortType) {
            IRootGridEntry pe = null;
 
            if (rgobjs == null || rgobjs.Length == 0) {
                return null;
            }
 
            try
            {
                if (rgobjs.Length == 1)
                {
                    pe = new SingleSelectRootGridEntry(view, rgobjs[0], baseProvider, currentHost, tab, initialSortType);
                }
                else
                {
                    pe = new MultiSelectRootGridEntry(view, rgobjs, baseProvider, currentHost, tab, initialSortType);
                }
            }
            catch (Exception e)
            {
                //Debug.fail("Couldn't create a top-level GridEntry");
                Debug.Fail(e.ToString());
                throw;
            }
            return pe;
        }
 
        /// <include file='doc\GridEntry.uex' path='docs/doc[@for="GridEntry.CreateChildren"]/*' />
        /// <devdoc>
        /// Populates the children of this grid entry
        /// </devdoc>
        protected virtual bool CreateChildren() {
            return CreateChildren(false);
        }
 
        /// <include file='doc\GridEntry.uex' path='docs/doc[@for="GridEntry.CreateChildren1"]/*' />
        /// <devdoc>
        /// Populates the children of this grid entry
        /// </devdoc>
        protected virtual bool CreateChildren(bool diffOldChildren) {
 
            Debug.Assert(!Disposed, "Why are we creating children after we are disposed?");
 
            if (!GetFlagSet(FL_EXPANDABLE)) {
                if (this.childCollection != null) {
                    this.childCollection.Clear();
                }
                else {
                    this.childCollection = new GridEntryCollection(this, new GridEntry[0]);
                }
                return false;
            }
 
 
            if (!diffOldChildren && childCollection != null && childCollection.Count > 0) {
                return true;
            }
 
 
            GridEntry [] childProps    = GetPropEntries(this,
                                                        this.PropertyValue,
                                                        this.PropertyType);
 
 
            bool fExpandable = (childProps != null && childProps.Length > 0);                                            
 
            if (diffOldChildren && childCollection != null && childCollection.Count > 0) {
                bool same = true;
                if (childProps.Length == childCollection.Count) {
                    for (int i = 0; i < childProps.Length; i++) {
                        if (!childProps[i].NonParentEquals(childCollection[i])) {
                            same = false;
                            break;
                        }
                    }
                }
                else {
                    same = false;
                }
 
                if (same) {
                    return true;
                }
            }
 
 
 
            if (!fExpandable) {
                SetFlag(FL_EXPANDABLE_FAILED,true);
                if (this.childCollection != null) {
                    this.childCollection.Clear();
                }
                else {
                    this.childCollection = new GridEntryCollection(this, new GridEntry[0]);
                }
 
                if (this.InternalExpanded) {
                    this.InternalExpanded = false;
                }
 
            }
            else {
                if (this.childCollection != null) {
                    this.childCollection.Clear();
                    this.childCollection.AddRange(childProps);
                }
                else {
                    this.childCollection = new GridEntryCollection(this, childProps);
                }
            }
            return fExpandable;
        }
 
        public void Dispose() {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
        protected virtual void Dispose(bool disposing) {
            // make sure we don't accidentally
            // check flags in this state...
            flags  |= FL_CHECKED;
 
            SetFlag(FLAG_DISPOSED, true);
            
            cacheItems = null;
            converter = null;
            editor = null;
            accessibleObject = null;
 
            if (disposing) {
                DisposeChildren();
            }
        }
 
 
        /// <include file='doc\GridEntry.uex' path='docs/doc[@for="GridEntry.DisposeChildren"]/*' />
        /// <devdoc>
        /// Disposes the array of children
        /// </devdoc>
        public virtual void DisposeChildren() {
            if (childCollection != null) {
                childCollection.Dispose();
                childCollection = null;
            }
        }
 
 
        ~GridEntry() {
            Dispose(false);
        }
 
 
        /// <include file='doc\GridEntry.uex' path='docs/doc[@for="GridEntry.EditPropertyValue"]/*' />
        /// <devdoc>
        /// Invokes the type editor for editing this item.
        /// </devdoc>
        internal virtual void EditPropertyValue(PropertyGridView iva) {
            if (UITypeEditor != null) {
                try {
                    // this is another icky part.  since edit value can push a modal loop
                    // there is a chance that this gridentry will be zombied before
                    // it returns.  make sure we're not disposed.
                    //
                    object originalValue = this.PropertyValue;
                    object value = UITypeEditor.EditValue(this, (IServiceProvider)(ITypeDescriptorContext)this, originalValue);
 
                    if (Disposed) {
                        return;
                    }
 
                    // Push the new value back into the property
                    if (value != originalValue && this.IsValueEditable) {
                        iva.CommitValue(this, value);
                    }
 
                    if (this.InternalExpanded) {
                        // QFE#3299: If edited property is expanded to show sub-properties, then we want to
                        // preserve the expanded states of it and all of its descendants. RecreateChildren()
                        // has logic that is supposed to do this, but which is fundamentally flawed.
                        PropertyGridView.GridPositionData positionData = GridEntryHost.CaptureGridPositionData();
                        this.InternalExpanded = false;
                        RecreateChildren();
                        positionData.Restore(GridEntryHost);
                    }
                    else {
                        // If edited property has no children or is collapsed, don't need to preserve expanded states.
                        // This limits the scope of the above QFE fix to just those cases where it is actually required.
                        RecreateChildren();
                    }
                }
                catch (Exception e)
                {
                    IUIService uiSvc = (IUIService)GetService(typeof(IUIService));
                    if (uiSvc != null)
                    {
                        uiSvc.ShowError(e);
                    }
                    else
                    {
                        RTLAwareMessageBox.Show(GridEntryHost, e.Message, SR.GetString(SR.PBRSErrorTitle), MessageBoxButtons.OK,
                                MessageBoxIcon.Error, MessageBoxDefaultButton.Button1, 0);
                    }
                }
            }
        }
 
        /// <include file='doc\GridEntry.uex' path='docs/doc[@for="GridEntry.Equals"]/*' />
        /// <devdoc>
        /// Tests two GridEntries for equality
        /// </devdoc>
        public override bool Equals(object obj) {
 
            if (NonParentEquals(obj)) {
                return((GridEntry)obj).ParentGridEntry == this.ParentGridEntry;
            }
            return false;
        }
 
        /// <include file='doc\GridEntry.uex' path='docs/doc[@for="GridEntry.FindPropertyValue"]/*' />
        /// <devdoc>
        /// Searches for a value of a given property for a value editor user
        /// </devdoc>
        public virtual object FindPropertyValue(string propertyName, Type propertyType) {
            object owner = GetValueOwner();
            PropertyDescriptor property = TypeDescriptor.GetProperties(owner)[propertyName];
            if (property != null && property.PropertyType == propertyType) {
                return property.GetValue(owner);
            }
 
            if (parentPE != null)
                return parentPE.FindPropertyValue(propertyName, propertyType);
 
            return null;
        }
 
        /// <include file='doc\GridEntry.uex' path='docs/doc[@for="GridEntry.GetChildIndex"]/*' />
        /// <devdoc>
        /// Returns the index of a child GridEntry
        /// </devdoc>
        internal virtual int GetChildIndex(GridEntry pe) {
            return this.Children.GetEntry(pe);
        }
 
        /// <include file='doc\GridEntry.uex' path='docs/doc[@for="GridEntry.GetComponents"]/*' />
        /// <devdoc>
        /// Gets the components that own the current value.  This is usually the value of the
        /// root entry, which is the object being browsed.  Walks up the GridEntry tree
        /// looking for an owner that is an IComponent
        /// </devdoc>
        public virtual IComponent[] GetComponents() {
            IComponent component = Component;
            if (component != null) {
                return new IComponent[] { component};
            }
            return null;
        }
 
        protected int GetLabelTextWidth(string labelText, Graphics g, Font f) {
 
            if (cacheItems == null) {
                cacheItems = new CacheItems();
            }
            else if (cacheItems.useCompatTextRendering == ownerGrid.UseCompatibleTextRendering && cacheItems.lastLabel == labelText && f.Equals(cacheItems.lastLabelFont)) {
                return cacheItems.lastLabelWidth;
            }
 
            SizeF textSize = PropertyGrid.MeasureTextHelper.MeasureText( this.ownerGrid, g, labelText, f);
 
            cacheItems.lastLabelWidth = (int) textSize.Width;
            cacheItems.lastLabel = labelText;
            cacheItems.lastLabelFont = f;
            cacheItems.useCompatTextRendering = ownerGrid.UseCompatibleTextRendering;
 
            return cacheItems.lastLabelWidth;
        }
 
        internal int GetValueTextWidth(string valueString, Graphics g, Font f) {
 
            if (cacheItems == null) {
               cacheItems = new CacheItems();
            } 
            else if (cacheItems.lastValueTextWidth != -1 && cacheItems.lastValueString == valueString && f.Equals(cacheItems.lastValueFont)) {
               return cacheItems.lastValueTextWidth;
            }
 
            // Value text is rendered using GDI directly (No TextRenderer) but measured/adjusted using GDI+ (since previous releases), so don't use MeasureTextHelper.
            cacheItems.lastValueTextWidth = (int) g.MeasureString(valueString, f).Width;
            cacheItems.lastValueString = valueString;
            cacheItems.lastValueFont = f;
            return cacheItems.lastValueTextWidth;
        }
        //subhag (66503) To check if text contains multiple lines
        //
        internal bool GetMultipleLines(string valueString) {
 
            if (valueString.IndexOf('\n') > 0  ||  valueString.IndexOf('\r') > 0 )
                return true;
            else 
                return false;
        }
        /// <include file='doc\GridEntry.uex' path='docs/doc[@for="GridEntry.GetValueOwner"]/*' />
        /// <devdoc>
        /// Gets the owner of the current value.  This is usually the value of the
        /// root entry, which is the object being browsed
        /// </devdoc>
        public virtual object GetValueOwner() {
            if (parentPE == null) {
                return this.PropertyValue;
            }
 
            return parentPE.GetChildValueOwner(this);
        }
 
        /// <include file='doc\GridEntry.uex' path='docs/doc[@for="GridEntry.GetValueOwners"]/*' />
        /// <devdoc>
        /// Gets the owners of the current value.  This is usually the value of the
        /// root entry, which is the objects being browsed for a multiselect item
        /// </devdoc>
        public virtual object[] GetValueOwners() {
            object owner = GetValueOwner();
            if (owner != null) {
                return new object[] { owner};
            }
            return null;
        }
 
        /// <include file='doc\GridEntry.uex' path='docs/doc[@for="GridEntry.GetChildValueOwner"]/*' />
        /// <devdoc>
        /// Gets the owner of the current value.  This is usually the value of the
        /// root entry, which is the object being browsed
        /// </devdoc>
        public virtual object GetChildValueOwner(GridEntry childEntry) {
 
            /*// make sure this is one of our children
            int index = GetChildIndex(childEntry);
 
            if (index != -1){
               return this.PropertyValue;
            }
 
 
            Debug.Fail(childEntry.PropertyLabel + " is not a child of " + this.PropertyLabel);
            return null;*/
            return this.PropertyValue;
        }
 
        /// <include file='doc\GridEntry.uex' path='docs/doc[@for="GridEntry.GetTestingInfo"]/*' />
        /// <devdoc>
        /// Returns a string with info about the currently selected GridEntry
        /// </devdoc>
        public virtual string GetTestingInfo() {
            string str = "object = (";
            string textVal = GetPropertyTextValue();
            if (textVal == null) {
                textVal = "(null)";
            }
            else {
                // make sure we clear any embedded nulls
                textVal = textVal.Replace((char)0, ' ');
            }
            Type type = this.PropertyType;
            if (type==null) {
                type = typeof(object);
            }
            str += this.FullLabel;
            str += "), property = (" + this.PropertyLabel + "," + type.AssemblyQualifiedName + "), value = " + "[" + textVal + "], expandable = " + this.Expandable.ToString() + ", readOnly = " + ShouldRenderReadOnly;;
            return str;
        }
 
        /// <include file='doc\GridEntry.uex' path='docs/doc[@for="GridEntry.GetValueType"]/*' />
        /// <devdoc>
        /// Retrieves the type of the value for this GridEntry
        /// </devdoc>
        public virtual Type GetValueType() {
            return this.PropertyType;
        }
 
        /// <include file='doc\GridEntry.uex' path='docs/doc[@for="GridEntry.GetPropEntries"]/*' />
        /// <devdoc>
        /// Returns the child GridEntries for this item.
        /// </devdoc>
        protected virtual GridEntry[] GetPropEntries(GridEntry peParent, object obj, Type objType) {
 
 
            // we don't want to create subprops for null objects.
            if (obj == null) {
                return null;
            }
 
            GridEntry[] entries = null;
 
            Attribute[] attributes = new Attribute[this.BrowsableAttributes.Count];
            this.BrowsableAttributes.CopyTo(attributes, 0);
 
            PropertyTab tab = this.CurrentTab;
            Debug.Assert(tab != null, "No current tab!");
 
            try
            {
 
                bool forceReadOnly = this.ForceReadOnly;
 
                if (!forceReadOnly)
                {
                    ReadOnlyAttribute readOnlyAttr = (ReadOnlyAttribute)TypeDescriptor.GetAttributes(obj)[typeof(ReadOnlyAttribute)];
                    forceReadOnly = (readOnlyAttr != null && !readOnlyAttr.IsDefaultAttribute());
                }
 
                // do we want to expose sub properties?
                //
                if (TypeConverter.GetPropertiesSupported(this) || AlwaysAllowExpand)
                {
 
                    // ask the tab if we have one.
                    //
                    PropertyDescriptorCollection props = null;
                    PropertyDescriptor defProp = null;
                    if (tab != null)
                    {
                        props = tab.GetProperties(this, obj, attributes);
                        defProp = tab.GetDefaultProperty(obj);
                    }
                    else
                    {
                        props = TypeConverter.GetProperties(this, obj, attributes);
                        defProp = TypeDescriptor.GetDefaultProperty(obj);
                    }
 
                    if (props == null)
                    {
                        return null;
                    }
 
                    if ((this.PropertySort & PropertySort.Alphabetical) != 0)
                    {
                        if (objType == null || !objType.IsArray)
                        {
                            props = props.Sort(GridEntry.DisplayNameComparer);
                        }
 
                        PropertyDescriptor[] propertyDescriptors = new PropertyDescriptor[props.Count];
                        props.CopyTo(propertyDescriptors, 0);
 
                        props = new PropertyDescriptorCollection(SortParenProperties(propertyDescriptors));
                    }
 
                    if (defProp == null && props.Count > 0)
                    {
                        defProp = props[0];
                    }
 
                    // if the target object is an array and nothing else has provided a set of
                    // properties to use, then expand the array.
                    //
                    if ((props == null || props.Count == 0) && objType != null && objType.IsArray && obj != null)
                    {
 
                        Array objArray = (Array)obj;
 
                        entries = new GridEntry[objArray.Length];
 
                        for (int i = 0; i < entries.Length; i++)
                        {
                            entries[i] = new ArrayElementGridEntry(this.ownerGrid, peParent, i);
                        }
                    }
                    else
                    {
                        // otherwise, create the proper GridEntries.
                        // 
                        bool createInstanceSupported = TypeConverter.GetCreateInstanceSupported(this);
                        entries = new GridEntry[props.Count];
                        int index = 0;
 
                        // loop through all the props we got and create property descriptors.
                        //
                        foreach (PropertyDescriptor pd in props)
                        {
                            GridEntry newEntry;
 
                            // make sure we've got a valid property, otherwise hide it
                            //
                            bool hide = false;
                            try
                            {
                                object owner = obj;
                                if (obj is ICustomTypeDescriptor)
                                {
                                    owner = ((ICustomTypeDescriptor)obj).GetPropertyOwner(pd);
                                }
                                pd.GetValue(owner);
                            }
                            catch (Exception w)
                            {
                                if (PbrsAssertPropsSwitch.Enabled)
                                {
                                    Debug.Fail("Bad property '" + peParent.PropertyLabel + "." + pd.Name + "': " + w.ToString());
                                }
                                hide = true;
                            }
 
                            if (createInstanceSupported)
                            {
                                newEntry = new ImmutablePropertyDescriptorGridEntry(this.ownerGrid, peParent, pd, hide);
                            }
                            else
                            {
                                newEntry = new PropertyDescriptorGridEntry(this.ownerGrid, peParent, pd, hide);
                            }
 
                            if (forceReadOnly)
                            {
                                newEntry.flags |= FLAG_FORCE_READONLY;
                            }
 
                            // check to see if we've come across the default item.
                            //
                            if (pd.Equals(defProp))
                            {
                                this.DefaultChild = newEntry;
                            }
 
                            // add it to the array.
                            //
                            entries[index++] = newEntry;
                        }
                    }
                }
            }
            catch (Exception e)
            {
#if DEBUG
                if (PbrsAssertPropsSwitch.Enabled) {
                    // Checked builds are not giving us enough information here.  So, output as much stuff as
                    // we can.
                    System.Text.StringBuilder b = new System.Text.StringBuilder();
                    b.Append(string.Format(CultureInfo.CurrentCulture, "********* Debug log written on {0} ************\r\n", DateTime.Now));
                    b.Append(string.Format(CultureInfo.CurrentCulture, "Exception '{0}' reading properties for object {1}.\r\n", e.GetType().Name, obj));
                    b.Append(string.Format(CultureInfo.CurrentCulture, "Exception Text: \r\n{0}", e.ToString()));
                    b.Append(string.Format(CultureInfo.CurrentCulture, "Exception stack: \r\n{0}", e.StackTrace));
                    string path = string.Format(CultureInfo.CurrentCulture, "{0}\\PropertyGrid.log", Environment.GetEnvironmentVariable("SYSTEMDRIVE"));
                    System.IO.FileStream s = new System.IO.FileStream(path, System.IO.FileMode.Append, System.IO.FileAccess.Write, System.IO.FileShare.None);
                    System.IO.StreamWriter w = new System.IO.StreamWriter(s);
                    w.Write(b.ToString());
                    w.Close();
                    s.Close();
                    RTLAwareMessageBox.Show(null, b.ToString(), SR.GetString(SR.PropertyGridInternalNoProp, path),
                        MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1, 0);
                }
#endif
                Debug.Fail("Failed to get properties: " + e.GetType().Name + "," + e.Message + "\n" + e.StackTrace);
            }
            return entries;
        }
 
        /// <include file='doc\GridEntry.uex' path='docs/doc[@for="GridEntry.ResetPropertyValue"]/*' />
        /// <devdoc>
        /// Resets the current item
        /// </devdoc>
        public virtual void ResetPropertyValue() {
            NotifyValue(NOTIFY_RESET);
            Refresh();
        }
 
        /*
        /// <summary>
        /// Checks if the value of the current item can be modified by the user.
        /// </summary>
        /// <returns>
        /// True if the value can be modified
        /// </returns>
        public virtual bool CanSetPropertyValue() {
            return 0 != (Flags & (GridEntry.FLAG_DROPDOWN_EDITABLE | GridEntry.FLAG_TEXT_EDITABLE | GridEntry.FLAG_CUSTOM_EDITABLE | GridEntry.FLAG_ENUMERABLE));
        }
        */
 
        /*
        /// <summary>
        /// Returns if it's an editable item.  An example of a readonly
        /// editable item is a collection property -- the property itself
        /// can not be modified, but it's value (e.g. it's children) can, so
        /// we don't want to draw it as readonly.
        /// </summary>
        /// <returns>
        /// True if the value associated with this property (e.g. it's children) can be modified even if it's readonly.
        /// </returns>
        public virtual bool CanSetReadOnlyPropertyValue() {
            return GetFlagSet(GridEntry.FLAG_READONLY_EDITABLE);
        }
        */
 
        /// <include file='doc\GridEntry.uex' path='docs/doc[@for="GridEntry.CanResetPropertyValue"]/*' />
        /// <devdoc>
        /// Returns if the property can be reset
        /// </devdoc>
        public virtual bool CanResetPropertyValue() {
            return NotifyValue(NOTIFY_CAN_RESET);
        }
 
        /// <include file='doc\GridEntry.uex' path='docs/doc[@for="GridEntry.DoubleClickPropertyValue"]/*' />
        /// <devdoc>
        /// Called when the item is double clicked.
        /// </devdoc>
        public virtual bool DoubleClickPropertyValue() {
            return NotifyValue(NOTIFY_DBL_CLICK);
        }
 
 
        /// <include file='doc\GridEntry.uex' path='docs/doc[@for="GridEntry.GetPropertyTextValue"]/*' />
        /// <devdoc>
        /// Returns the text value of this property.
        /// </devdoc>
        public virtual string GetPropertyTextValue() {
            return GetPropertyTextValue(this.PropertyValue);
        }
 
        /// <include file='doc\GridEntry.uex' path='docs/doc[@for="GridEntry.GetPropertyTextValue1"]/*' />
        /// <devdoc>
        /// Returns the text value of this property.
        /// </devdoc>
        public virtual string GetPropertyTextValue(object value) {
            string str = null;
 
            TypeConverter converter = TypeConverter;
            try
            {
                str = converter.ConvertToString(this, value);
            }
            catch (Exception t)
            {
                Debug.Fail("Bad Type Converter! " + t.GetType().Name + ", " + t.Message + "," + converter.ToString(), t.ToString());
            }
 
            if (str == null) {
                str = String.Empty;
            }
            return str;
        }
 
        /// <include file='doc\GridEntry.uex' path='docs/doc[@for="GridEntry.GetPropertyValueList"]/*' />
        /// <devdoc>
        /// Returns the text values of this property.
        /// </devdoc>
        public virtual object[] GetPropertyValueList() {
            ICollection values = TypeConverter.GetStandardValues(this);
            if (values != null) {
                object[] valueArray = new object[values.Count];
                values.CopyTo(valueArray, 0);
                return valueArray;
            }
            return new object[0];
        }
 
        public override int GetHashCode() {
            // These can be null, so workaround giving hashcode = 0 for null objects.
            object label = this.PropertyLabel;
            object type = this.PropertyType;
            UInt32 h1 = (UInt32)((label == null)  ? 0 : label.GetHashCode());
            UInt32 h2 = (UInt32)((type == null)   ? 0 : type.GetHashCode());
            UInt32 h3 = (UInt32)GetType().GetHashCode();
 
            return(int)(h1 ^ ((h2 << 13) | (h2 >> 19)) ^ ((h3 << 26) | (h3 >> 6)));
        }
 
        /// <include file='doc\GridEntry.uex' path='docs/doc[@for="GridEntry.GetFlagSet"]/*' />
        /// <devdoc>
        /// Checks if a given flag is set
        /// </devdoc>
        protected virtual bool GetFlagSet(int flag) {
            return((flag & Flags) != 0);
        }
 
        protected Font GetFont(bool boldFont) {
            if (boldFont)
                return GridEntryHost.GetBoldFont();
            else
                return GridEntryHost.GetBaseFont();
        }
 
        protected IntPtr GetHfont(bool boldFont) {
            if (boldFont)
                return GridEntryHost.GetBoldHfont();
            else
                return GridEntryHost.GetBaseHfont();
        }
 
        /// <include file='doc\GridEntry.uex' path='docs/doc[@for="GridEntry.GetService"]/*' />
        /// <devdoc>
        ///      Retrieves the requested service.  This may
        ///      return null if the requested service is not
        ///      available.
        /// </devdoc>
        public virtual object GetService(Type serviceType) {
        
            if (serviceType == typeof(GridItem)) {
                return (GridItem)this;
            }
            
            if (parentPE != null) {
                return parentPE.GetService(serviceType);
            }
            return null;
        }
 
        internal virtual bool NonParentEquals(object obj) {
            if (obj == this) return true;
            if (obj == null) return false;
            if (!(obj is GridEntry)) return false;
            GridEntry pe = (GridEntry)obj;
 
            return pe.PropertyLabel.Equals(this.PropertyLabel) &&
                   pe.PropertyType.Equals(this.PropertyType) && pe.PropertyDepth == this.PropertyDepth; 
        }
 
 
        /// <include file='doc\GridEntry.uex' path='docs/doc[@for="GridEntry.PaintLabel"]/*' />
        /// <devdoc>
        /// Paints the label portion of this GridEntry into the given Graphics object.  This
        /// is called by the GridEntry host (the PropertyGridView) when this GridEntry is
        /// to be painted.
        /// </devdoc>
        public virtual void PaintLabel(System.Drawing.Graphics g, Rectangle rect, Rectangle clipRect, bool selected, bool paintFullLabel) {
            PropertyGridView gridHost = this.GridEntryHost;
            Debug.Assert(gridHost != null, "No propEntryHost");
            string strLabel = this.PropertyLabel;
            
            int borderWidth = gridHost.GetOutlineIconSize()+OUTLINE_ICON_PADDING;
 
            // fill the background if necessary
            Brush bkBrush = selected ? gridHost.GetSelectedItemWithFocusBackBrush(g) : this.GetBackgroundBrush(g);
            // if we don't have focus, paint with the line color
            if (selected && !hasFocus) {
                bkBrush = gridHost.GetLineBrush(g);
            }
 
            bool fBold = ((this.Flags & GridEntry.FLAG_LABEL_BOLD) != 0);
            Font font = GetFont(fBold);
 
            int labelWidth = GetLabelTextWidth(strLabel, g, font);
 
            int neededWidth = paintFullLabel ? labelWidth : 0;
            int stringX = rect.X + this.PropertyLabelIndent;
            Brush blank = bkBrush;
 
            if (paintFullLabel && (neededWidth >= (rect.Width-(stringX+2)))) {
                int totalWidth = stringX + neededWidth + PropertyGridView.GDIPLUS_SPACE; // 5 = extra needed to ensure text draws completely and isn't clipped.
#if PBRS_PAINT_DEBUG
                blank = Brushes.Green;
#endif
 
                // blank out the space we're going to use
                g.FillRectangle(blank, borderWidth-1, rect.Y, totalWidth-borderWidth+3, rect.Height);
 
                // draw an end line
                Pen linePen = new Pen(gridHost.GetLineColor());
                g.DrawLine(linePen, totalWidth, rect.Y, totalWidth, rect.Height);
                linePen.Dispose();
 
                // set the new width that we can draw into
                rect.Width = totalWidth - rect.X;
            }
            else { // Normal case -- no pseudo-tooltip for the label
 
#if PBRS_PAINT_DEBUG
                blank = Brushes.Red;
#endif
                // Debug.WriteLine(rect.X.ToString() +" "+ rect.Y.ToString() +" "+ rect.Width.ToString() +" "+ rect.Height.ToString());
                g.FillRectangle(blank, rect.X, rect.Y, rect.Width, rect.Height);
            }
            
            // draw the border stripe on the left
            Brush stripeBrush = gridHost.GetLineBrush(g);
            g.FillRectangle(stripeBrush, rect.X, rect.Y, borderWidth, rect.Height);
      
            if (selected && hasFocus) {
                g.FillRectangle(gridHost.GetSelectedItemWithFocusBackBrush(g), stringX, rect.Y, rect.Width - stringX - 1, rect.Height);
            }
            
            int maxSpace = Math.Min(rect.Width-stringX-1, labelWidth + PropertyGridView.GDIPLUS_SPACE);
            Rectangle textRect = new Rectangle(stringX, rect.Y + 1, maxSpace, rect.Height - 1);
            
 
            if (!Rectangle.Intersect(textRect, clipRect).IsEmpty)  {
                Region oldClip = g.Clip;
                g.SetClip(textRect);
                                               
                //We need to Invert color only if in Highcontrast mode, targeting 4.7.1 and above, Gridcategory and not a developer override. This is required to achieve required contrast ratio.
                var shouldInvertForHC = colorInversionNeededInHC && (fBold || (selected && !hasFocus));
 
                // Do actual drawing
                // A brush is needed if using GDI+ only (UseCompatibleTextRendering); if using GDI, only the color is needed.
                Color textColor = selected && hasFocus ? gridHost.GetSelectedItemWithFocusForeColor() : shouldInvertForHC ? InvertColor(ownerGrid.LineColor) : g.GetNearestColor(this.LabelTextColor);
             
                if( this.ownerGrid.UseCompatibleTextRendering ) {
                    using( Brush textBrush = new SolidBrush(textColor)){
                        StringFormat stringFormat = new StringFormat(StringFormatFlags.NoWrap);
                        stringFormat.Trimming = StringTrimming.None;
                        g.DrawString(strLabel, font, textBrush, textRect, stringFormat);
                    }
                }
                else{
                    TextRenderer.DrawText( g, strLabel, font, textRect, textColor, PropertyGrid.MeasureTextHelper.GetTextRendererFlags() );
                }
    #if PBRS_PAINT_DEBUG
                textRect.Width --;
                textRect.Height--;
                g.DrawRectangle(new Pen(Color.Blue), textRect);
    #endif
                g.SetClip(oldClip, CombineMode.Replace);
                oldClip.Dispose();   // clip is actually copied out.
 
                if (maxSpace <= labelWidth) {
                    this.labelTipPoint = new Point(stringX+2, rect.Y+1);
                }
                else {
                    this.labelTipPoint = InvalidPoint;
                }
            }
 
            rect.Y -= 1;
            rect.Height += 2;
 
            PaintOutline(g, rect);
        }
 
        /// <include file='doc\GridEntry.uex' path='docs/doc[@for="GridEntry.PaintOutline"]/*' />
        /// <devdoc>
        /// Paints the outline portion of this GridEntry into the given Graphics object.  This
        /// is called by the GridEntry host (the PropertyGridView) when this GridEntry is
        /// to be painted.
        /// </devdoc>
        public virtual void PaintOutline(System.Drawing.Graphics g, Rectangle r) {
            // draw tree-view glyphs as triangles on Vista and Windows afterword
            // when Vistual style is enabled
            if (GridEntryHost.IsExplorerTreeSupported) {
 
                // size of Explorer Tree style glyph (triangle) is different from +/- glyph,
                // so when we change the visual style (such as changing Windows theme),
                // we need to recaculate outlineRect 
                if(!lastPaintWithExplorerStyle) {
                    outlineRect = Rectangle.Empty;
                    lastPaintWithExplorerStyle = true;
                }
 
                PaintOutlineWithExplorerTreeStyle(g, r, (DpiHelper.EnableDpiChangedHighDpiImprovements && GridEntryHost!=null) ? this.GridEntryHost.HandleInternal: IntPtr.Zero);
            }
            // draw tree-view glyphs as +/-
            else {
 
                // size of Explorer Tree style glyph (triangle) is different from +/- glyph,
                // so when we change the visual style (such as changing Windows theme),
                // we need to recaculate outlineRect 
                if (lastPaintWithExplorerStyle) {
                    outlineRect = Rectangle.Empty;
                    lastPaintWithExplorerStyle = false;
                }
 
                PaintOutlineWithClassicStyle(g, r);
            }
        }
 
        private void PaintOutlineWithExplorerTreeStyle(System.Drawing.Graphics g, Rectangle r, IntPtr handle) {
            if (this.Expandable) {
                bool fExpanded = this.InternalExpanded;
                Rectangle outline = this.OutlineRect;
 
                // make sure we're in our bounds
                outline = Rectangle.Intersect(r, outline);
                if (outline.IsEmpty) return;
 
                VisualStyleElement element = null;
                if (fExpanded)
                    element = VisualStyleElement.ExplorerTreeView.Glyph.Opened;
                else
                    element = VisualStyleElement.ExplorerTreeView.Glyph.Closed;
                
                // Invert color if it is not overriden by developer.
                if (colorInversionNeededInHC) {
                    Color textColor = InvertColor(ownerGrid.LineColor);
                    if (g != null) {
                        Brush b = new SolidBrush(textColor);
                        g.FillRectangle(b, outline);
                        b.Dispose();
                    }
                }               
 
                VisualStyleRenderer explorerTreeRenderer = new VisualStyleRenderer(element);
                explorerTreeRenderer.DrawBackground(g, outline, handle);
            }
        }
 
        private void PaintOutlineWithClassicStyle(System.Drawing.Graphics g, Rectangle r) {
            // draw outline box.
            if (this.Expandable) {
                bool fExpanded = this.InternalExpanded;
                Rectangle outline = this.OutlineRect;
 
                // make sure we're in our bounds
                outline = Rectangle.Intersect(r, outline);
                if (outline.IsEmpty) return;
 
                // draw border area box
                Brush b = this.GetBackgroundBrush(g);
                Pen p;
                Color penColor = GridEntryHost.GetTextColor();
 
                // inverting text color to back ground to get required contrast ratio
                if (colorInversionNeededInHC) {
                    penColor = InvertColor(ownerGrid.LineColor);
                }
                else { 
                    // Filling rectangle as it was in all cases where we do not invert colors.
                    g.FillRectangle(b, outline); 
                }
                
 
                if (penColor.IsSystemColor) {
                    p = SystemPens.FromSystemColor(penColor);
                }
                else {
                    p = new Pen(penColor);
                }
 
                g.DrawRectangle(p, outline.X, outline.Y, outline.Width - 1, outline.Height - 1);
 
                // draw horizontal line for +/-
                int indent = 2;
                g.DrawLine(p, outline.X + indent,outline.Y + outline.Height / 2,
                           outline.X + outline.Width - indent - 1,outline.Y + outline.Height/2);
 
                // draw vertical line to make a +
                if (!fExpanded) {
                    g.DrawLine(p, outline.X + outline.Width/2, outline.Y + indent,
                               outline.X + outline.Width/2, outline.Y + outline.Height - indent - 1);
                }
 
                if (!penColor.IsSystemColor) {
                    p.Dispose();
                }
            }
        }
 
        /// <include file='doc\GridEntry.uex' path='docs/doc[@for="GridEntry.PaintValue"]/*' />
        /// <devdoc>
        /// Paints the value portion of this GridEntry into the given Graphics object.  This
        /// is called by the GridEntry host (the PropertyGridView) when this GridEntry is
        /// to be painted.
        /// </devdoc>
        public virtual void PaintValue(object val, System.Drawing.Graphics g, Rectangle rect, Rectangle clipRect, PaintValueFlags paintFlags) { 
            PropertyGridView gridHost = this.GridEntryHost;
            Debug.Assert(gridHost != null, "No propEntryHost");
            int cPaint = 0;
 
            Color textColor = gridHost.GetTextColor();
            if (this.ShouldRenderReadOnly) {
                textColor = GridEntryHost.GrayTextColor;
            }
            
            string strValue;
            
            if ((paintFlags & PaintValueFlags.FetchValue) != PaintValueFlags.None) {
               if (cacheItems != null && cacheItems.useValueString) {
                  strValue = cacheItems.lastValueString;
                  val = cacheItems.lastValue;
               }
               else {
                  val = this.PropertyValue;
                  strValue = GetPropertyTextValue(val);
                  if (cacheItems == null) {
                     cacheItems = new CacheItems();
                  }
                  cacheItems.lastValueString = strValue;
                  cacheItems.useValueString = true;
                  cacheItems.lastValueTextWidth = -1;
                  cacheItems.lastValueFont = null;
                  cacheItems.lastValue = val;
               }
            }
            else {
               strValue = GetPropertyTextValue(val);                  
            }
 
            // paint out the main rect using the appropriate brush
            Brush bkBrush = this.GetBackgroundBrush(g);
            Debug.Assert(bkBrush != null, "We didn't find a good background brush for PaintValue");
 
            if ((paintFlags & PaintValueFlags.DrawSelected) != PaintValueFlags.None) {
                bkBrush = gridHost.GetSelectedItemWithFocusBackBrush(g);
                textColor = gridHost.GetSelectedItemWithFocusForeColor();
            }
 
            Brush blank = bkBrush;
#if PBRS_PAINT_DEBUG
            blank = Brushes.Yellow;
#endif
            //g.FillRectangle(blank, rect.X-1, rect.Y, rect.Width+1, rect.Height);
            g.FillRectangle(blank, clipRect);
            
            if (IsCustomPaint) {
                cPaint = gridHost.GetValuePaintIndent();
                Rectangle rectPaint = new Rectangle(rect.X + 1, rect.Y + 1, gridHost.GetValuePaintWidth(), gridHost.GetGridEntryHeight() - 2);
                
                if (!Rectangle.Intersect(rectPaint, clipRect).IsEmpty) {
                   UITypeEditor uie = UITypeEditor;
                   if (uie != null) {
                       uie.PaintValue(new PaintValueEventArgs(this, val, g, rectPaint));
                   }
   
                   // paint a border around the area
                   rectPaint.Width --;
                   rectPaint.Height--;
                   g.DrawRectangle(SystemPens.WindowText, rectPaint);
                }
            }
 
            rect.X += cPaint + gridHost.GetValueStringIndent();
            rect.Width -= cPaint + 2 * gridHost.GetValueStringIndent();
 
            // bold the property if we need to persist it (e.g. it's not the default value)
            bool valueModified = ((paintFlags & PaintValueFlags.CheckShouldSerialize) != PaintValueFlags.None) && ShouldSerializePropertyValue();
            
            // If we have text to paint, paint it
            if (strValue != null && strValue.Length > 0) {
                
                Font f = GetFont(valueModified);
 
                if (strValue.Length > maximumLengthOfPropertyString)
                {
                    strValue = strValue.Substring(0, maximumLengthOfPropertyString);
                }
                int textWidth = GetValueTextWidth(strValue, g, f);
                bool doToolTip = false;
                
                //subhag (66503) To check if text contains multiple lines
                //
                if (textWidth >= rect.Width ||  GetMultipleLines(strValue)) 
                     doToolTip = true;
                               
                if (Rectangle.Intersect(rect, clipRect).IsEmpty) {
                     return;
                }
                
                // Do actual drawing
                
                //strValue = ReplaceCRLF(strValue);
                
                
                 // AS/URT  55015
                // bump the text down 2 pixels and over 1 pixel.
                if ((paintFlags & PaintValueFlags.PaintInPlace) != PaintValueFlags.None) { 
                    rect.Offset(1, 2);
                }
                else {
                    // only go down one pixel when we're painting in the listbox
                    rect.Offset(1, 1);
                } 
 
                Matrix m = g.Transform;
                IntPtr hdc = g.GetHdc();
                IntNativeMethods.RECT lpRect = IntNativeMethods.RECT.FromXYWH(rect.X + (int)m.OffsetX + 2, rect.Y + (int)m.OffsetY - 1, rect.Width - 4, rect.Height);
                IntPtr hfont = GetHfont(valueModified);
                
                int oldTextColor = 0;
                int oldBkColor = 0;
 
                Color bkColor = ((paintFlags & PaintValueFlags.DrawSelected) != PaintValueFlags.None) ? GridEntryHost.GetSelectedItemWithFocusBackColor() : GridEntryHost.BackColor;
                
                try {
                    oldTextColor = SafeNativeMethods.SetTextColor(new HandleRef(g, hdc), SafeNativeMethods.RGBToCOLORREF(textColor.ToArgb()));
                    oldBkColor = SafeNativeMethods.SetBkColor(new HandleRef(g, hdc), SafeNativeMethods.RGBToCOLORREF(bkColor.ToArgb()));
                    hfont = SafeNativeMethods.SelectObject(new HandleRef(g, hdc), new HandleRef(null, hfont));
                    int format = IntNativeMethods.DT_EDITCONTROL | IntNativeMethods.DT_EXPANDTABS | IntNativeMethods.DT_NOCLIP | IntNativeMethods.DT_SINGLELINE | IntNativeMethods.DT_NOPREFIX;
                    if (gridHost.DrawValuesRightToLeft) {
                        format |= IntNativeMethods.DT_RIGHT | IntNativeMethods.DT_RTLREADING;
                    }
 
                    // For password mode, Replace the string value either with * or a bullet, depending on the OS platform
                    if (ShouldRenderPassword) {
 
                        if (passwordReplaceChar == (char)0) {
                            if (Environment.OSVersion.Version.Major > 4) {
                                passwordReplaceChar = (char)0x25CF; // Bullet is 2022, but edit box uses round circle 25CF
                            }
                            else {
                                passwordReplaceChar = '*';
                            }
                        }
 
                        strValue = new string(passwordReplaceChar, strValue.Length);
                    }
 
                    IntUnsafeNativeMethods.DrawText(new HandleRef(g, hdc), strValue, ref lpRect, format);
                }
                finally {
                    SafeNativeMethods.SetTextColor(new HandleRef(g, hdc), oldTextColor);
                    SafeNativeMethods.SetBkColor(new HandleRef(g, hdc), oldBkColor);
                    hfont = SafeNativeMethods.SelectObject(new HandleRef(g, hdc), new HandleRef(null, hfont));
                    g.ReleaseHdcInternal(hdc);
                }
                
                
                #if PBRS_PAINT_DEBUG
                    rect.Width --;
                    rect.Height--;
                    g.DrawRectangle(new Pen(Color.Purple), rect);
               #endif
               
                if (doToolTip) {
                    this.ValueToolTipLocation = new Point(rect.X+2, rect.Y-1);
                }
                else {
                     this.ValueToolTipLocation = InvalidPoint;
                }
            }
 
            return;
        }
 
        public virtual bool OnComponentChanging() {
            if (ComponentChangeService != null) {
                try {
                    ComponentChangeService.OnComponentChanging(GetValueOwner(), PropertyDescriptor);
                }
                catch (CheckoutException coEx) {
                    if (coEx == CheckoutException.Canceled) {
                        return false;
                    }
                    throw coEx;
                }
            }
            return true;
        }
 
        public virtual void OnComponentChanged() {
            if (ComponentChangeService != null) {
                ComponentChangeService.OnComponentChanged(GetValueOwner(), PropertyDescriptor, null, null);
            }
        }
 
        /// <include file='doc\GridEntry.uex' path='docs/doc[@for="GridEntry.OnLabelClick"]/*' />
        /// <devdoc>
        /// Called when the label portion of this GridEntry is clicked.
        /// Default implmentation fired the event to any listeners, so be sure
        /// to call base.OnLabelClick(e) if this is overrideen.
        /// </devdoc>
        protected virtual void OnLabelClick(EventArgs e) {
            RaiseEvent(EVENT_LABEL_CLICK, e);
        }
 
        /// <include file='doc\GridEntry.uex' path='docs/doc[@for="GridEntry.OnLabelDoubleClick"]/*' />
        /// <devdoc>
        /// Called when the label portion of this GridEntry is double-clicked.
        /// Default implmentation fired the event to any listeners, so be sure
        /// to call base.OnLabelDoubleClick(e) if this is overrideen.
        /// </devdoc>
        protected virtual void         OnLabelDoubleClick(EventArgs e) {
            RaiseEvent(EVENT_LABEL_DBLCLICK, e);
        }
 
        /// <include file='doc\GridEntry.uex' path='docs/doc[@for="GridEntry.OnMouseClick"]/*' />
        /// <devdoc>
        /// Called when the GridEntry is clicked.
        /// </devdoc>
        public virtual bool OnMouseClick(int x, int y, int count, MouseButtons button) {
            // where are we at?
            PropertyGridView gridHost = this.GridEntryHost;
            Debug.Assert(gridHost != null, "No prop entry host!");
 
            // make sure it's the left button
            if ((button & MouseButtons.Left) != MouseButtons.Left) {
                return false;
            }
 
 
            int labelWidth  = gridHost.GetLabelWidth();
 
            // are we in the label?
            if (x >= 0 && x <= labelWidth) {
 
                // are we on the outline?
                if (Expandable) {
                    Rectangle outlineRect = OutlineRect;
                    if (outlineRect.Contains(x, y)) {
                        if (count % 2 == 0) {
                            OnOutlineDoubleClick(EventArgs.Empty);
                        }
                        else {
                            OnOutlineClick(EventArgs.Empty);
                        }
                        return true;
                    }
                }
 
                if (count % 2 == 0) {
                    OnLabelDoubleClick(EventArgs.Empty);
                }
                else {
                    OnLabelClick(EventArgs.Empty);
                }
                return true;
            }
 
            // are we in the value?
            labelWidth += gridHost.GetSplitterWidth();
            if (x >= labelWidth  && x <= labelWidth + gridHost.GetValueWidth()) {
                if (count % 2 == 0) {
                    OnValueDoubleClick(EventArgs.Empty);
                }
                else {
                    OnValueClick(EventArgs.Empty);
                }
                return true;
            }
            return false;
        }
 
        /// <include file='doc\GridEntry.uex' path='docs/doc[@for="GridEntry.OnOutlineClick"]/*' />
        /// <devdoc>
        /// Called when the outline icon portion of this GridEntry is clicked.
        /// Default implmentation fired the event to any listeners, so be sure
        /// to call base.OnOutlineClick(e) if this is overrideen.
        /// </devdoc>
        protected virtual void OnOutlineClick(EventArgs e) {
            RaiseEvent(EVENT_OUTLINE_CLICK, e);
        }
 
        /// <include file='doc\GridEntry.uex' path='docs/doc[@for="GridEntry.OnOutlineDoubleClick"]/*' />
        /// <devdoc>
        /// Called when the outline icon portion of this GridEntry is double-clicked.
        /// Default implmentation fired the event to any listeners, so be sure
        /// to call base.OnOutlineDoubleClick(e) if this is overrideen.
        /// </devdoc>
        protected virtual void OnOutlineDoubleClick(EventArgs e) {
            RaiseEvent(EVENT_OUTLINE_DBLCLICK, e);
        }
 
        /// <include file='doc\GridEntry.uex' path='docs/doc[@for="GridEntry.OnRecreateChildren"]/*' />
        /// <devdoc>
        /// Called when RecreateChildren is called.
        /// Default implmentation fired the event to any listeners, so be sure
        /// to call base.OnOutlineDoubleClick(e) if this is overrideen.
        /// </devdoc>
        protected virtual void OnRecreateChildren(GridEntryRecreateChildrenEventArgs e) {
            Delegate handler = GetEventHandler(EVENT_RECREATE_CHILDREN);
            if (handler != null) ((GridEntryRecreateChildrenEventHandler)handler)(this, e);
        }
 
        /// <include file='doc\GridEntry.uex' path='docs/doc[@for="GridEntry.OnValueClick"]/*' />
        /// <devdoc>
        /// Called when the value portion of this GridEntry is clicked.
        /// Default implmentation fired the event to any listeners, so be sure
        /// to call base.OnValueClick(e) if this is overrideen.
        /// </devdoc>
        protected virtual void OnValueClick(EventArgs e) {
            RaiseEvent(EVENT_VALUE_CLICK, e);
        }
 
        /// <include file='doc\GridEntry.uex' path='docs/doc[@for="GridEntry.OnValueDoubleClick"]/*' />
        /// <devdoc>
        /// Called when the value portion of this GridEntry is clicked.
        /// Default implmentation fired the event to any listeners, so be sure
        /// to call base.OnValueDoubleClick(e) if this is overrideen.
        /// </devdoc>
        protected virtual void OnValueDoubleClick(EventArgs e) {
            RaiseEvent(EVENT_VALUE_DBLCLICK, e);
        }
 
 
        
        internal bool OnValueReturnKey() {
            return NotifyValue(NOTIFY_RETURN);
        }
 
        /// <include file='doc\GridEntry.uex' path='docs/doc[@for="GridEntry.SetFlag"]/*' />
        /// <devdoc>
        /// Sets the specified flag
        /// </devdoc>
        protected virtual void SetFlag(int flag, bool fVal) {
            SetFlag(flag, (fVal ? flag : 0));
        }
 
        /// <include file='doc\GridEntry.uex' path='docs/doc[@for="GridEntry.SetFlag1"]/*' />
        /// <devdoc>
        /// Sets the default child of this entry, given a valid value mask.
        /// </devdoc>
        protected virtual void SetFlag(int flag_valid, int flag, bool fVal) {
            SetFlag(flag_valid | flag,
                    flag_valid | (fVal ? flag : 0));
        }
 
        /// <include file='doc\GridEntry.uex' path='docs/doc[@for="GridEntry.SetFlag2"]/*' />
        /// <devdoc>
        /// Sets the value of a flag
        /// </devdoc>
        protected virtual void SetFlag(int flag, int val) {
            Flags = (Flags & ~(flag)) | val;
        }
        
        public override bool Select() {
            if (Disposed) {
                return false;
            }
            
            try {
                GridEntryHost.SelectedGridEntry = this;
                return true;
            }
            catch {
            }
            return false;
        }
 
        /// <include file='doc\GridEntry.uex' path='docs/doc[@for="GridEntry.ShouldSerializePropertyValue"]/*' />
        /// <devdoc>
        /// Checks if this value should be persisited.
        /// </devdoc>
        internal virtual bool ShouldSerializePropertyValue() {
            
            if (cacheItems != null) {
                if (cacheItems.useShouldSerialize) {
                     return cacheItems.lastShouldSerialize;
                }
                else {
                    cacheItems.lastShouldSerialize = NotifyValue(NOTIFY_SHOULD_PERSIST);
                    cacheItems.useShouldSerialize = true;
                }
            }
            else {
               cacheItems = new CacheItems();
               cacheItems.lastShouldSerialize = NotifyValue(NOTIFY_SHOULD_PERSIST);
               cacheItems.useShouldSerialize = true;
            }
            return cacheItems.lastShouldSerialize;
        }
 
        private PropertyDescriptor[] SortParenProperties(PropertyDescriptor[] props) {
 
            PropertyDescriptor[] newProps = null;
            int newPos = 0;
 
 
            // first scan the list and move any parentesized properties to the front.
            for (int i = 0; i < props.Length; i++) {
                if (((ParenthesizePropertyNameAttribute)props[i].Attributes[typeof(ParenthesizePropertyNameAttribute)]).NeedParenthesis) {
                    if (newProps == null) {
                        newProps = new PropertyDescriptor[props.Length];
                    }
                    newProps[newPos++] = props[i];
                    props[i] = null;
                }
            }
 
 
            // second pass, copy any that didn't have the parens.
            if (newPos > 0) {
                for (int i = 0; i < props.Length; i++) {
                    if (props[i] != null) {
                        newProps[newPos++] = props[i];
                    }
                } 
                props = newProps;
            }
            return props;
        }
 
        /// <include file='doc\GridEntry.uex' path='docs/doc[@for="GridEntry.NotifyValueGivenParent"]/*' />
        /// <devdoc>
        /// Sends a notify message to this GridEntry, and returns the success result
        /// </devdoc>
        internal virtual bool NotifyValueGivenParent(object obj, int type) {
            return false;
        }
 
        /// <include file='doc\GridEntry.uex' path='docs/doc[@for="GridEntry.NotifyChildValue"]/*' />
        /// <devdoc>
        /// Sends a notify message to the child GridEntry, and returns the success result
        /// </devdoc>
        internal virtual bool NotifyChildValue(GridEntry pe, int type) {
            
            return pe.NotifyValueGivenParent(pe.GetValueOwner(),type);
        }
 
        internal virtual bool NotifyValue(int type) {
            // KILLME, Microsoft, more spagetti
            if (parentPE == null) {
                return true;
            }
            else {
                return parentPE.NotifyChildValue(this, type);
            }
        }
 
        internal void RecreateChildren() {
            RecreateChildren(-1);
        }
        
        internal void RecreateChildren(int oldCount) {
        
            // cause the flags to be rebuilt as well...
            bool wasExpanded = this.InternalExpanded || oldCount > 0;
            
            if (oldCount == -1) {
                oldCount = this.VisibleChildCount;
            }
            
            ResetState();
            if (oldCount == 0) {
                return;
            }
            
            foreach(GridEntry child in ChildCollection) {
                child.RecreateChildren();
            }
            
            DisposeChildren();
            this.InternalExpanded = wasExpanded;
            OnRecreateChildren(new GridEntryRecreateChildrenEventArgs(oldCount, VisibleChildCount));
        }
 
        /// <include file='doc\GridEntry.uex' path='docs/doc[@for="GridEntry.Refresh"]/*' />
        /// <devdoc>
        /// Refresh the current GridEntry's value and it's children
        /// </devdoc>
        public virtual void Refresh() {
 
            Type type = this.PropertyType;
            if (type != null && type.IsArray) {
                CreateChildren(true);
            }
 
            if (this.childCollection != null) {
            
                // check to see if the value has changed.
                //
                if (this.InternalExpanded && cacheItems != null && cacheItems.lastValue != null && cacheItems.lastValue != this.PropertyValue) {
                    ClearCachedValues();
                    RecreateChildren();
                    return;
                }
                else if (this.InternalExpanded) {
                    // otherwise just do a refresh.
                    IEnumerator childEnum = childCollection.GetEnumerator();
                    while (childEnum.MoveNext()) {
                        object o = childEnum.Current;
                        Debug.Assert(o != null, "Collection contains a null element.  But how? Garbage collector hole?  GDI+ corrupting memory?");
                        GridEntry e = (GridEntry) o;
                        e.Refresh();
                    }
                }
                else {
                    DisposeChildren();
                }
            }
 
            ClearCachedValues();
        }
 
        public virtual void         RemoveOnLabelClick(EventHandler h) {
            RemoveEventHandler(EVENT_LABEL_CLICK, h);
        }
        public virtual void         RemoveOnLabelDoubleClick(EventHandler h) {
            RemoveEventHandler(EVENT_LABEL_DBLCLICK, h);
        }
 
        public virtual void         RemoveOnValueClick(EventHandler h) {
            RemoveEventHandler(EVENT_VALUE_CLICK, h);
        }
 
        public virtual void         RemoveOnValueDoubleClick(EventHandler h) {
            RemoveEventHandler(EVENT_VALUE_DBLCLICK, h);
        }
 
        public virtual void         RemoveOnOutlineClick(EventHandler h) {
            RemoveEventHandler(EVENT_OUTLINE_CLICK, h);
        }
 
        public virtual void         RemoveOnOutlineDoubleClick(EventHandler h) {
            RemoveEventHandler(EVENT_OUTLINE_DBLCLICK, h);
        }
 
        public virtual void         RemoveOnRecreateChildren(GridEntryRecreateChildrenEventHandler h) {
            RemoveEventHandler(EVENT_RECREATE_CHILDREN, h);
        }
 
        /*
        private string ReplaceCRLF(string str) {
            str = str.Replace('\r', (char)1);
            str = str.Replace('\n', (char)1);
            return str;
        }
        */
 
        protected void ResetState() {
            this.Flags = 0;
            ClearCachedValues();
        }
 
        /// <include file='doc\GridEntry.uex' path='docs/doc[@for="GridEntry.SetPropertyTextValue"]/*' />
        /// <devdoc>
        /// Sets the value of this GridEntry from text
        /// </devdoc>
        public virtual bool SetPropertyTextValue(string str) {
            bool fChildrenPrior = (childCollection != null && childCollection.Count > 0);
            this.PropertyValue = ConvertTextToValue(str);
            CreateChildren();
            bool fChildrenAfter = (childCollection != null && childCollection.Count > 0);
            return(fChildrenPrior != fChildrenAfter);
        }
 
        public override string ToString() {
            return GetType().FullName + " " + this.PropertyLabel;
        }
 
 
 
#if !DONT_SUPPORT_ADD_EVENT_HANDLER
        private EventEntry eventList;
 
        protected virtual void AddEventHandler(object key, Delegate handler) {
            // Locking 'this' here is ok since this is an internal class.  See VSW#464499.
            lock(this) {
                if (handler == null) return;
                for (EventEntry e = eventList; e != null; e = e.next) {
                    if (e.key == key) {
                        e.handler = Delegate.Combine(e.handler, handler);
                        return;
                    }
                }
                eventList = new EventEntry(eventList, key, handler);
            }
        }
 
        protected virtual void RaiseEvent(object key, EventArgs e) {
            Delegate handler = GetEventHandler(key);
            if (handler != null) ((EventHandler)handler)(this, e);
        }
 
        protected virtual Delegate GetEventHandler(object key) {
            // Locking 'this' here is ok since this is an internal class.  See VSW#464499.
            lock(this) {
                for (EventEntry e = eventList; e != null; e = e.next) {
                    if (e.key == key) return e.handler;
                }
                return null;
            }
        }
 
        protected virtual void RemoveEventHandler(object key, Delegate handler) {
            // Locking this here is ok since this is an internal class.  See VSW#464499.
            lock(this) {
                if (handler == null) return;
                for (EventEntry e = eventList, prev = null; e != null; prev = e, e = e.next) {
                    if (e.key == key) {
                        e.handler = Delegate.Remove(e.handler, handler);
                        if (e.handler == null) {
                            if (prev == null) {
                                eventList = e.next;
                            }
                            else {
                                prev.next = e.next;
                            }
                        }
                        return;
                    }
                }
            }
        }
 
        protected virtual void RemoveEventHandlers() {
            eventList = null;
        }
 
        /// <include file='doc\GridEntry.uex' path='docs/doc[@for="GridEntry.EventEntry"]/*' />
        /// <devdoc>
        /// </devdoc>
        private sealed class EventEntry {
            internal EventEntry next;
            internal object key;
            internal Delegate handler;
 
            internal EventEntry(EventEntry next, object key, Delegate handler) {
                this.next = next;
                this.key = key;
                this.handler = handler;
            }
        }
#endif
 
        [ComVisible(true)]
        public class GridEntryAccessibleObject : AccessibleObject {
 
            protected GridEntry owner = null;
            private delegate void SelectDelegate(AccessibleSelection flags);
            private int[] runtimeId = null; // Used by UIAutomation
 
            public GridEntryAccessibleObject(GridEntry owner) : base() {
                Debug.Assert(owner != null, "GridEntryAccessibleObject must have a valid owner GridEntry");
                this.owner = owner;
            }
 
            public override Rectangle Bounds {
                get {
                    return PropertyGridView.AccessibilityGetGridEntryBounds(owner);
                }
            }
 
            public override string DefaultAction {
                get {
                    if (!owner.Expandable) {
                        return base.DefaultAction;
                    }
                    else if (owner.Expanded) {
                        return SR.GetString(SR.AccessibleActionCollapse);
                    }
                    else {
                        return SR.GetString(SR.AccessibleActionExpand);
                    }
                }
            }
 
            public override string Description {
                get {
                    return owner.PropertyDescription;
                }
            }
 
            public override string Help {
                get {
                    if (AccessibilityImprovements.Level1) {
                        return owner.PropertyDescription;
                    }
                    else {
                        return base.Help;
                    }
                }
            }
 
            /// <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) {
                    switch(direction) {
                        case UnsafeNativeMethods.NavigateDirection.Parent:
                            var parentGridEntry = owner.ParentGridEntry;
                            if (parentGridEntry != null) {
                                if (parentGridEntry is SingleSelectRootGridEntry) {
                                    return owner.OwnerGrid.GridViewAccessibleObject;
                                }
                                else {
                                    return parentGridEntry.AccessibilityObject;
                                }
                            }
 
                            return Parent;
                        case UnsafeNativeMethods.NavigateDirection.PreviousSibling:
                            return Navigate(AccessibleNavigation.Previous);
                        case UnsafeNativeMethods.NavigateDirection.NextSibling:
                            return Navigate(AccessibleNavigation.Next);
                    }
                }
 
                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 (PropertyGridView.PropertyGridViewAccessibleObject)Parent;
                    }
 
                    return base.FragmentRoot;
                }
            }
 
            #region IAccessibleEx - patterns and properties
 
            internal override bool IsIAccessibleExSupported() {
                if (owner.Expandable && AccessibilityImprovements.Level1) {
                    return true;
                }
                else {
                    return false;
                }
            }
 
            internal override int[] RuntimeId {
                get {
                    if (runtimeId == null) {
                        // we need to provide a unique ID
                        // others are implementing this in the same manner
                        // first item is static - 0x2a
                        // second item can be anything, but it's good to supply HWND
                        // third and others are optional, but in case of GridItem we need it, to make it unique
                        // grid items are not controls, they don't have hwnd - we use hwnd of PropertyGridView
 
                        runtimeId = new int[3];
                        runtimeId[0] = 0x2a;
                        runtimeId[1] = (int)(long)owner.GridEntryHost.Handle;
                        runtimeId[2] = this.GetHashCode();
                    }
 
                    return runtimeId;
                }
            }
 
            internal override object GetPropertyValue(int propertyID) {
                switch (propertyID) {
                    case NativeMethods.UIA_NamePropertyId:
                        return Name;
                    case NativeMethods.UIA_ControlTypePropertyId:
                        if (AccessibilityImprovements.Level3) {
                            // In Level 3 the accessible hierarchy is changed so we cannot use Button type
                            // for the grid items to not break automation logic that searches for the first
                            // button in the PropertyGridView to show dialog/drop-down. In Level < 3 action
                            // button is one of the first children of PropertyGridView.
                            return NativeMethods.UIA_DataItemControlTypeId;
                        }
 
                        return NativeMethods.UIA_ButtonControlTypeId;
                    case NativeMethods.UIA_IsExpandCollapsePatternAvailablePropertyId:
                        return (Object)IsPatternSupported(NativeMethods.UIA_ExpandCollapsePatternId);
                }
 
                if (AccessibilityImprovements.Level3) {
                    switch (propertyID) {
                        case NativeMethods.UIA_AccessKeyPropertyId:
                            return string.Empty;
                        case NativeMethods.UIA_HasKeyboardFocusPropertyId:
                            return owner.hasFocus;
                        case NativeMethods.UIA_IsKeyboardFocusablePropertyId:
                            return (this.State & AccessibleStates.Focusable) == AccessibleStates.Focusable;
                        case NativeMethods.UIA_IsEnabledPropertyId:
                            return true;
                        case NativeMethods.UIA_AutomationIdPropertyId:
                            return GetHashCode().ToString();
                        case NativeMethods.UIA_HelpTextPropertyId:
                            return Help ?? string.Empty;
                        case NativeMethods.UIA_IsPasswordPropertyId:
                            return false;
                        case NativeMethods.UIA_IsOffscreenPropertyId:
                            return (this.State & AccessibleStates.Offscreen) == AccessibleStates.Offscreen;
                        case NativeMethods.UIA_LegacyIAccessibleRolePropertyId:
                            return Role;
                        case NativeMethods.UIA_LegacyIAccessibleDefaultActionPropertyId:
                            return DefaultAction;
                        default:
                            return base.GetPropertyValue(propertyID);
                    }
                }
 
                return null;
            }
 
            internal override bool IsPatternSupported(int patternId) {
                if (owner.Expandable &&
                    patternId == NativeMethods.UIA_ExpandCollapsePatternId) {
                    return true;
                }
 
                if (AccessibilityImprovements.Level3 && (
                    patternId == NativeMethods.UIA_InvokePatternId ||
                    patternId == NativeMethods.UIA_LegacyIAccessiblePatternId)) {
                    return true;
                }
 
                return false;
            }
 
            internal override void Expand() {
                if (owner.Expandable && owner.Expanded == false) {
                    owner.Expanded = true;
                }
            }
 
            internal override void Collapse() {
                if (owner.Expandable && owner.Expanded == true) {
                    owner.Expanded = false;
                }
            }
 
            internal override UnsafeNativeMethods.ExpandCollapseState ExpandCollapseState {
                get {
                    if (owner.Expandable) {
                        return owner.Expanded ? UnsafeNativeMethods.ExpandCollapseState.Expanded : UnsafeNativeMethods.ExpandCollapseState.Collapsed;
                    }
                    else {
                        return UnsafeNativeMethods.ExpandCollapseState.LeafNode;
                    }
                }
            }
 
            #endregion
 
            [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)]
            public override void DoDefaultAction() {
                owner.OnOutlineClick(EventArgs.Empty);
            }
 
            public override string Name {
                get {
                    return owner.PropertyLabel;
                }
            }
 
            public override AccessibleObject Parent {
                [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)]
                get {
                    return((Control)this.owner.GridEntryHost).AccessibilityObject;
                }
            }
 
            private PropertyGridView PropertyGridView {
                get {
                    return(PropertyGridView)((PropertyGridView.PropertyGridViewAccessibleObject)Parent).Owner;
                }
            }
 
            public override AccessibleRole Role {
                get {
                    if (AccessibilityImprovements.Level3) {
                        return AccessibleRole.Cell;
                    }
                    else if (AccessibilityImprovements.Level1)  {
                        if (owner.Expandable) {
                            return AccessibleRole.ButtonDropDownGrid;
                        }
                        else {
                            return AccessibleRole.Cell;
                        }
                    }
 
                    return AccessibleRole.Row;
                }
            }
 
            public override AccessibleStates State {
                get {
                    AccessibleStates state = AccessibleStates.Selectable | AccessibleStates.Focusable;
 
                    // Determine focus
                    //
                    if (owner.Focus) {
                        state |= AccessibleStates.Focused;
                    }
 
                    // Determine selected
                    //
                    Debug.Assert(Parent != null, "GridEntry AO does not have a parent AO");
                    PropertyGridView.PropertyGridViewAccessibleObject parent = (PropertyGridView.PropertyGridViewAccessibleObject)Parent;
                    if (parent.GetSelected() == this) {
                        state |= AccessibleStates.Selected;
                    }
                    
                    // Determine expanded/collapsed state
                    //
                    if (owner.Expandable) {
                        if (owner.Expanded) {
                            state |= AccessibleStates.Expanded;
                        }
                        else {
                            state |= AccessibleStates.Collapsed;
                        }
                    }
 
                    // Determine readonly/editable state
                    //
                    if (owner.ShouldRenderReadOnly) {
                        state |= AccessibleStates.ReadOnly;
                    }
 
                    // Determine password state
                    //
                    if (owner.ShouldRenderPassword) {
                        state |= AccessibleStates.Protected;
                    }
 
                    return state;
                }
            }
 
            public override string Value {
                [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)]
                get {
                    return owner.GetPropertyTextValue();
                }
 
                [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)]
                set {
                    owner.SetPropertyTextValue(value);
                }                
            }
 
            /// <include file='doc\GridEntry.uex' path='docs/doc[@for="GridEntry.GridEntryAccessibleObject.GetFocused"]/*' />
            /// <devdoc>
            ///      Returns the currently focused child, if any.
            ///      Returns this if the object itself is focused.
            /// </devdoc>
            public override AccessibleObject GetFocused() {
 
                if (owner.Focus) {
                    return this;
                }
                else {
                    return null;
                }
            }
 
 
            /// <include file='doc\GridEntry.uex' path='docs/doc[@for="GridEntry.GridEntryAccessibleObject.Navigate"]/*' />
            /// <devdoc>
            ///      Navigate to the next or previous grid entry.
            /// </devdoc>
            [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)]
            public override AccessibleObject Navigate(AccessibleNavigation navdir) {
 
                PropertyGridView.PropertyGridViewAccessibleObject parent =
                (PropertyGridView.PropertyGridViewAccessibleObject)Parent;
 
                switch (navdir) {
                    case AccessibleNavigation.Down:
                    case AccessibleNavigation.Right:
                    case AccessibleNavigation.Next:
                        return parent.Next(this.owner);
 
                    case AccessibleNavigation.Up:
                    case AccessibleNavigation.Left:
                    case AccessibleNavigation.Previous:
                        return parent.Previous(this.owner);
 
                    case AccessibleNavigation.FirstChild:
                    case AccessibleNavigation.LastChild:
                        // Fall through and return null,
                        // as this object has no children.
                        break;
                }
 
                return null;
 
            }
 
            [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)]
            public override void Select(AccessibleSelection flags) {
 
                // vs  77963 -- make sure we're on the right thread.
                //
                if (PropertyGridView.InvokeRequired) {
                    PropertyGridView.Invoke(new SelectDelegate(this.Select), new object[]{flags});
                    return;
                }
 
                // Focus the PropertyGridView window
                //
                if ( (flags & AccessibleSelection.TakeFocus) == AccessibleSelection.TakeFocus) {
                    bool focused = PropertyGridView.FocusInternal();
                }
 
                // Select the grid entry
                //
                if ( (flags & AccessibleSelection.TakeSelection) == AccessibleSelection.TakeSelection) {
                    PropertyGridView.AccessibilitySelect(this.owner);
                }
            }
 
            internal override void SetFocus() {
                base.SetFocus();
 
                if (AccessibilityImprovements.Level3) {
                    RaiseAutomationEvent(NativeMethods.UIA_AutomationFocusChangedEventId);
                }
            }
        }
 
        public class DisplayNameSortComparer : IComparer {
            public int Compare(object left, object right) {
		// review: (Microsoft) Is CurrentCulture correct here?  This was already reviewed as invariant...
                return String.Compare(((PropertyDescriptor)left).DisplayName, ((PropertyDescriptor)right).DisplayName, true, CultureInfo.CurrentCulture);
            }
        }
    }
 
    internal class AttributeTypeSorter : IComparer{
 
        private static IDictionary typeIds;
 
        private static string GetTypeIdString(Attribute a) {
 
            string result;
            object typeId = a.TypeId;
 
 
            if (typeId == null) {
                Debug.Fail("Attribute '" + a.GetType().FullName + "' does not have a typeid."); 
                return "";
            }
 
            if (typeIds == null) {
                typeIds = new Hashtable();
                result = null;
            }
            else {
                result = typeIds[typeId] as string;
            }
 
            if (result == null) {
                result = typeId.ToString();
                typeIds[typeId] = result;
            }
            return result;
        }
 
        public int Compare(object obj1, object obj2) {
            Attribute a1 = obj1 as Attribute;
            Attribute a2 = obj2 as Attribute;
        
            if (a1 == null && a2 == null) {
                return 0;
            }
            else if (a1 == null) {
                return -1;
            }
            else if (a2 == null) {
                return 1;
            }
            return String.Compare(AttributeTypeSorter.GetTypeIdString(a1), AttributeTypeSorter.GetTypeIdString(a2), false, CultureInfo.InvariantCulture);
        }
    }
    
    internal delegate void GridEntryRecreateChildrenEventHandler(object sender, GridEntryRecreateChildrenEventArgs rce);
    
    internal class GridEntryRecreateChildrenEventArgs : EventArgs {
        public readonly int OldChildCount;
        public readonly int NewChildCount;
        
        public GridEntryRecreateChildrenEventArgs(int oldCount, int newCount) {
            this.OldChildCount = oldCount;
            this.NewChildCount = newCount;
        }
    }
 
}