// This is a part of the Smart PropertyGrid Library
// Copyright (C) 2001-2006 VisualHint
// All rights reserved.
//
// This source code can be used, distributed or modified
// only under terms and conditions
// of the accompanying license agreement.

using System;
using System.Reflection;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Globalization;
using System.Collections.Generic;
using System.Drawing.Design;

namespace VisualHint.SmartPropertyGrid
{
    /// <summary>
    /// Represents the value part of a property.
    /// </summary>
    public class PropertyValue
    {
        internal event EventHandler<PropertyChangedEventArgs> PropertyChangedEvent;

        public static int UITypeEditorCustomDrawing = 20;

        protected Type mUnderlyingType;

        public virtual Type UnderlyingType
        {
            get
            {
                if (mUnderlyingType == null)
                {
                    object obj = UnderlyingValue;
                    mUnderlyingType = (obj != null ? obj.GetType() : null);
                }

                return mUnderlyingType;
            }
        }

        public virtual bool HasMultipleValues
        {
            get { return false; }
        }

        protected object mUnderlyingValue;

		internal virtual object UnderlyingValue
		{
			set
            {
                mUnderlyingValue = value;
            }

            get { return mUnderlyingValue; }
		}

        private bool _noLinkWithChildren = false;

        /// <summary>
        /// Sets or gets a boolean indicating if a parent property's value has a link with the values of
        /// the child properties.
        /// </summary>
        public bool NoLinkWithChildren
        {
            set { _noLinkWithChildren = value; }
            get { return _noLinkWithChildren; }
        }

        private CultureInfo _cultureInfo = null;

        public CultureInfo CultureInfo
        {
            get
            {
                if (_cultureInfo == null)
                    _cultureInfo = (CultureInfo)CultureInfo.CurrentCulture.Clone();

                return _cultureInfo;
            }

            set
            {
                _cultureInfo = value;
            }
        }

        private int _id;

        internal int Id
        {
            set { _id = value; }
            get { return _id; }
        }

        private object _additionalValueKey;

        internal object AdditionalValueKey
        {
            set { _additionalValueKey = value; }
            get { return _additionalValueKey; }
        }

        private bool _previousValueValid = true;

        public bool PreviousValueValid
        {
            set { _previousValueValid = value; }
            get { return _previousValueValid; }
        }

        private object _previousValue;

        public object PreviousValue
        {
            set { _previousValue = value; _previousValueValid = !HasMultipleValues; }
            
            get
            {
                if (_previousValueValid == false)
                    throw new MultipleValuesException();

                return _previousValue;
            }
        }

        private int _editboxLeftMargin = -1;

        public int EditboxLeftMargin
        {
            get { return _editboxLeftMargin; }
        }

        private object _tag;

        /// <summary>
        /// Sets or gets an external user data that is attached to this value.
        /// </summary>
        /// <remarks>The client application is free to attach any value or object to a property Value.</remarks>
        public object Tag
        {
            set { _tag = value; }
            get { return _tag; }
        }

        private Font _font;

        public Font Font
        {
            get { return (_font == null ? mOwnerEnumerator.Property.Font : _font); }
            set
            {
                _font = value;
                FontSetOnProperty(_font);
            }
        }

        internal void FontSetOnProperty(Font font)
        {
            TextBox tb = new TextBox();
            tb.Font = _font;
            Win32Calls.SendMessage(tb.Handle, Win32Calls.EM_SETMARGINS, 0x0003, Win32Calls.MakeLong(0xffff, 0xffff));
            _editboxLeftMargin = 2 + Win32Calls.LoWord(Win32Calls.SendMessage(tb.Handle, Win32Calls.EM_GETMARGINS, 0, 0));

            if (mOwnerEnumerator.Property.Selected)
            {
                Control inPlaceControl = mOwnerEnumerator.Property.ParentGrid.InPlaceControl;
                if (inPlaceControl != null)
                {
                    inPlaceControl.Font = _font;

                    Rectangle itemRect = mOwnerEnumerator.Property.ParentGrid.Grid.GetItemRect(mOwnerEnumerator);
                    Rectangle valueRect = mOwnerEnumerator.Property.GetValueRect(itemRect);
                    Feel.MoveControl(valueRect, mOwnerEnumerator, true);
                }
            }
        }

        private Color _foreColor;

        public Color ForeColor
        {
            get
            {
//                if (mOwnerEnumerator.Property.Value.IsReadOnly(mOwnerEnumerator) && (Grid.Enabled || ((Grid.DisableMode & PropertyGrid.DisableModes.EverythingGrayedOut) == 0)))
  //                  return DisabledForeColor;

                if (_foreColor.IsEmpty)
                    return mOwnerEnumerator.Property.ForeColor;
                else
                    return _foreColor;
            }

            set
            {
                _foreColor = value;
                if (mOwnerEnumerator.Property.Selected)
                {
                    Control inPlaceControl = mOwnerEnumerator.Property.ParentGrid.InPlaceControl;
                    if (inPlaceControl != null)
                        inPlaceControl.ForeColor = _foreColor;
                }
            }
        }

        private Color _disabledForeColor;

        public Color DisabledForeColor
        {
            get
            {
                if (_disabledForeColor.IsEmpty)
                    return mOwnerEnumerator.Property.DisabledForeColor;
                else
                    return _disabledForeColor;
            }

            set
            {
                _disabledForeColor = value;
                if (mOwnerEnumerator.Property.Selected)
                {
                    Control inPlaceControl = mOwnerEnumerator.Property.ParentGrid.InPlaceControl;
                    if (inPlaceControl != null)
                        inPlaceControl.ForeColor = _disabledForeColor;
                }
            }
        }

        private Color _readOnlyForeColor;

        public Color ReadOnlyForeColor
        {
            get
            {
                if (_readOnlyForeColor.IsEmpty)
                    return mOwnerEnumerator.Property.ReadOnlyForeColor;
                else
                    return _readOnlyForeColor;
            }

            set
            {
                _readOnlyForeColor = value;
                if (mOwnerEnumerator.Property.Selected)
                {
                    Control inPlaceControl = mOwnerEnumerator.Property.ParentGrid.InPlaceControl;
                    if (inPlaceControl != null)
                        inPlaceControl.ForeColor = _readOnlyForeColor;
                }
            }
        }

        private Color _backColor;

        public Color BackColor
        {
            get
            {
                if (_backColor.IsEmpty)
                    return mOwnerEnumerator.Property.BackColor;
                else
                    return _backColor;
            }

            set
            {
                _backColor = value;
                if (mOwnerEnumerator.Property.Selected)
                {
                    Control inPlaceControl = mOwnerEnumerator.Property.ParentGrid.InPlaceControl;
                    if (inPlaceControl != null)
                        inPlaceControl.BackColor = _backColor;
                }
            }
        }

        private Size _imageSize = Size.Empty;

        public Size ImageSize
        {
            get { return _imageSize; }
            set { _imageSize = value; }
        }

        private ImageList _imageList = null;

        public ImageList ImageList
        {
            set
            {
                _imageList = value;

                if (HasMultipleValues == false)
                {
                    string[] displayedValues = GetDisplayedValues();
                    if (displayedValues.Length == 0)
                    {
                        DisplayedValuesNeededEventArgs e = new DisplayedValuesNeededEventArgs(OwnerEnumerator);
                        OwnerEnumerator.Property.ParentGrid.OnDisplayedValuesNeeded(e);

                        if (e.DisplayedValues == null)
                            displayedValues = new string[0];
                        else
                            displayedValues = e.DisplayedValues;
                    }

                    int index = 0;
                    foreach (object str in displayedValues)
                    {
                        if (str.ToString() == DisplayString)
                        {
                            ImageIndex = index;
                            break;
                        }

                        index++;
                    }
                }
                else
                    _imageIndex = -1;
            }

            get { return _imageList; }
        }

        private int _imageIndex;

        public int ImageIndex
        {
            set { _imageIndex = value; }
            get { return _imageIndex; }
        }

		private PropertyLook _look;

        /// <summary>
        /// Sets of gets the Look instance that is used to paint a property value when no inplace control is shown.
        /// </summary>
        /// <remarks>
        /// When the look is assigned, it may modify the height of the property if it can handle it (for example
        /// a radiobutton look will modify it based on the possible displayed values). It may also set the feel
        /// to null if instructed to do so (see <see cref="Look.NoFeelForReadOnly"/>).
        /// </remarks>
        public PropertyLook Look
        {
            set
            {
                _look = value;
                if (_look != null)
                {
                    _look.Value = this;
                    _look.AttachProperty(mOwnerEnumerator);
                    _look.ControlHeightMultiplier();

                    if (IsReadOnly(mOwnerEnumerator) && _look.NoFeelForReadOnly && (mPropCtrl.ReadOnlyVisual == PropertyGrid.ReadOnlyVisuals.ReadOnlyFeel))
                        Feel = null;
                }
            }

            get { return _look; }
        }

        private PropertyFeel _feel;

        /// <summary>
        /// Sets of gets the Feel instance that is used to display an inplace control when the property is selected.
        /// </summary>
        /// <remarks>
        /// To let the user edit a property, you must assign a feel to it. Using this property is a way to proceed.
        /// Because a feel is a singleton, you don't create yourself a feel object. Instead you request from the
        /// PropertyGrid a previously registered feel by using <see cref="PropertyGrid.GetRegisteredFeel"/>.
        /// <para>The other ways to assign a feel to a property are explained in <see cref="PropertyFeelAttribute"/> and
        /// in <see cref="PropertyGrid.RegisterFeelAttachment"/>.</para>
        /// </remarks>
        /// <example>
        /// <code>
        /// propEnum.Property.Feel = GetRegisteredFeel(PropertyGrid.FeelEdit);
        /// </code>
        /// </example>
        public PropertyFeel Feel
        {
            set { _feel = value; }

            get
            {
                if (ReadOnly && (Look != null) && (Look.NoFeelForReadOnly))
                    return null;

                if (!Grid.Grid.Enabled && ((Grid.DisableMode & PropertyGrid.DisableModes.Simple) != 0) &&
                    (Grid.ReadOnlyVisual == PropertyGrid.ReadOnlyVisuals.Disabled))
                    return null;

                return _feel;
            }
        }

        private char _groupedValueSeparator = ';';

        public char GroupedValueSeparator
        {
            get { return _groupedValueSeparator; }
            set { _groupedValueSeparator = value; }
        }

        // A pointer to the parent PropertyGrid.
		protected PropertyGrid mPropCtrl;

        public PropertyGrid Grid
        {
            set { mPropCtrl = value; }
            get { return mPropCtrl; }
        }

        private bool _drawingDoneByEditor = false;

        public bool DrawingDoneByEditor
        {
            get { return _drawingDoneByEditor; }
        }

        private UITypeEditor _uiTypeEditor;

        protected PropertyEnumerator mOwnerEnumerator;

        public PropertyEnumerator OwnerEnumerator
        {
            set
            {
                mOwnerEnumerator = (PropertyEnumerator)value.Clone();

                ExtractDisplayedValues();

                // This call is to simulate a value change report to the parent so that the parent value
                // can be updated while creating and initializing this value.
                if ((mOwnerEnumerator.Parent.Property != null) && (mOwnerEnumerator.Parent.Property.Value != null) &&
                    (mOwnerEnumerator.Parent.Property.Value.NoLinkWithChildren == false))
                    mOwnerEnumerator.Parent.Property.Value.OnChildValueChanged(this);

                _uiTypeEditor = (UITypeEditor)PropertyDescriptor.GetEditor(typeof(UITypeEditor));
                if (_uiTypeEditor == null)
                {
                    NullableConverter nc = mOwnerEnumerator.Property.Value.TypeConverter as NullableConverter;
                    if (nc != null)
                        _uiTypeEditor = (UITypeEditor)TypeDescriptor.GetEditor(nc.UnderlyingType, typeof(UITypeEditor));
                }

                if (_uiTypeEditor == null)
                {
                    // Try to find a UITypeEditor attached to the property with an attribute
                    EditorAttribute eAttr = (EditorAttribute)mOwnerEnumerator.Property.Value.GetAttribute(typeof(EditorAttribute));
                    if (eAttr == null)
                    {
                        foreach (object obj in mOwnerEnumerator.Property.Value.UnderlyingType.GetCustomAttributes(typeof(EditorAttribute), true))
                        {
                            if (obj is EditorAttribute)
                            {
                                eAttr = (EditorAttribute)obj;
                                break;
                            }
                        }
                    }

                    if (eAttr != null)
                    {
                        Type[] typeArray = new Type[] { typeof(Type) };
                        Type editorType = Utilities.GetTypeFromName(eAttr.EditorTypeName, mOwnerEnumerator.Property.Value.UnderlyingType);
                        if (editorType == typeof(UITypeEditor))
                        {
                            if (editorType.GetConstructor(typeArray) != null)
                                _uiTypeEditor = (UITypeEditor)TypeDescriptor.CreateInstance(null, editorType, typeArray, new object[] { mOwnerEnumerator.Property.Value.UnderlyingType });
                            else
                                _uiTypeEditor = (UITypeEditor)TypeDescriptor.CreateInstance(null, editorType, null, null);
                        }
                    }
                }

                // Try to find a UITypeEditor attached to the Type
                if (_uiTypeEditor == null)
                    _uiTypeEditor = (UITypeEditor)TypeDescriptor.GetEditor(UnderlyingType, typeof(UITypeEditor));

                PropertyTypeDescriptorContext context = GetTypeDescriptorContext(mOwnerEnumerator);
                if ((_uiTypeEditor != null) && !_uiTypeEditor.GetType().Equals(typeof(UITypeEditor)) &&
                    (_uiTypeEditor.GetPaintValueSupported(context)))
                {
                    if ((_uiTypeEditor.GetEditStyle() == UITypeEditorEditStyle.None) &&
                        mOwnerEnumerator.Property.Value.TypeConverter.GetStandardValuesSupported(context))
                        _drawingDoneByEditor = true;
                }
            }

            get
            {
                return mOwnerEnumerator;
            }
        }

        public virtual string DisplayName
        {
            get { return ""; }
        }

        public virtual string Name
        {
            get { return mOwnerEnumerator.Property.DisplayName; }
        }

        private Dictionary<Type, List<Attribute>> _customAttributes = new Dictionary<Type, List<Attribute>>();

        private PropertyValue _parentValue;

        protected PropertyValue ParentValue
        {
            get { return _parentValue; }
        }

        internal Attribute[] Attributes
        {
            set
            {
                if ((value != null) && (value.Length > 0))
                {
                    foreach (Attribute attr in value)
                    {
                        AttributeUsageAttribute attrUsage = (AttributeUsageAttribute)TypeDescriptor.GetAttributes(attr)
                            [typeof(AttributeUsageAttribute)];
                        if (attrUsage.AllowMultiple == false)
                            SetSingleAttribute(attr);
                        else
                        {
                            List<Attribute> oneTypeAttributes;
                            if (_customAttributes.ContainsKey(attr.GetType()) == false)
                            {
                                oneTypeAttributes = new List<Attribute>();
                                _customAttributes[attr.GetType()] = oneTypeAttributes;
                            }
                            else
                                oneTypeAttributes = _customAttributes[attr.GetType()];

                            oneTypeAttributes.Add(attr);
                        }
                    }
                }
            }
        }

        internal void SetSingleAttribute(Attribute attr)
        {
            List<Attribute> oneTypeAttributes;
            if (_customAttributes.TryGetValue(attr.GetType(), out oneTypeAttributes) == false)
            {
                oneTypeAttributes = new List<Attribute>();
                _customAttributes[attr.GetType()] = oneTypeAttributes;
            }
            else
                oneTypeAttributes.Clear();

            oneTypeAttributes.Add(attr);
        }

        internal void DeleteSingleAttribute(Attribute attr)
        {
            _customAttributes.Remove(attr.GetType());
        }

        /// <summary>
        /// 
        /// </summary>
        public bool ReadOnly
        {
            get
            {
                return IsReadOnly(mOwnerEnumerator);
            }

            set
            {
                ReadOnlyAttribute attr = (ReadOnlyAttribute)GetAttribute(typeof(ReadOnlyAttribute));
                bool change = false;
                if (attr != null)
                {
                    if (value && (attr.IsReadOnly == false))
                    {
                        SetSingleAttribute(ReadOnlyAttribute.Yes);
                        change = true;
                    }
                    else if ((value == false) && attr.IsReadOnly)
                    {
                        SetSingleAttribute(ReadOnlyAttribute.No);
                        change = true;
                    }
                }
                else
                {
                    if (value && !ReadOnly)
                    {
                        SetSingleAttribute(ReadOnlyAttribute.Yes);
                        change = true;
                    }
                    else if ((value == false) && ReadOnly)
                    {
                        SetSingleAttribute(ReadOnlyAttribute.No);
                        change = true;
                    }
                }

                if (change)
                {
                    PropertyGrid pgrid = OwnerEnumerator.Property.ParentGrid;

                    if (ReadOnly)
                    {
                        if (pgrid.ReadOnlyVisual == PropertyGrid.ReadOnlyVisuals.Disabled)
                            pgrid.EnableProperty(OwnerEnumerator, false);
                        else
                            pgrid.Grid.Invalidate();
                    }
                    else
                    {
                        pgrid.EnableProperty(OwnerEnumerator, true);
                    }
                }
            }
        }

        private PropertyValidatorBase _validator = null;

        public PropertyValue()
        {
            _foreColor = Color.Empty;
            _backColor = Color.Empty;
            _disabledForeColor = Color.Empty;
        }

        public PropertyValue(PropertyValue parentValue) : this()
        {
            _parentValue = parentValue;
        }

        public PropertyValidatorBase Validator
        {
            set { _validator = value; }
            get { return _validator; }
        }

        protected virtual bool ValidateValue(object value)
        {
            return true;
        }

        public bool SetValue(object value)
        {
            object valueToSet = value;

            // For validation purposes, let's transform the string value into a typed object
            if (value is string)
            {
                string str = (valueToSet as string);
                if (_look != null)
                    str = _look.TransformDisplayString(str);

                valueToSet = GetValueToValidate(GetActualString(str));
            }

            // Validate the value and make the change if it is valid
            if (ValidateValue(valueToSet))
                return SetValue(valueToSet, ValueUpdateSource.FromOutside);

            return false;
        }

        protected void RecreateChildProperties()
        {
//            Grid.Grid.RecreateChildProperties(OwnerEnumerator);

            // In the case of an array for example, when the array changed, all the child properties
            // must be deleted and recreated.
            if (TypeConverter.GetPropertiesSupported() && !TypeConverter.GetCreateInstanceSupported())
            {
                bool expanded = OwnerEnumerator.Property.Expanded;
                
                PropertyEnumerator propEnum = OwnerEnumerator.Children;
                while (propEnum != propEnum.RightBound)
                {
                    PropertyEnumerator nextEnum = ((PropertyEnumerator)propEnum.Clone()).MoveNext();
                    Grid.Grid.DeletePropertyInternal(propEnum);
                    propEnum = nextEnum;
                }

                Grid.Grid.AddChildProperties(OwnerEnumerator);

                Grid.Grid.CloseEmptySpace();
                Grid.Grid.CheckScrollbar();
            }
        }

        protected internal bool SetValue(object value, ValueUpdateSource updateSource)
        {
            bool result = SetValueInternal(value, updateSource);

            if (result && (mOwnerEnumerator != null)) // mOwnerEnumerator could be null for the boolean of Property._enabledVariable
            {
                RecreateChildProperties();

                // Inform its parent that this value has changed
                if (updateSource != ValueUpdateSource.FromParent)
                {
                    if ((mOwnerEnumerator.Parent.Property != null) && (mOwnerEnumerator.Parent.Property.Value != null) &&
                        (mOwnerEnumerator.Parent.Property.Value.NoLinkWithChildren == false))
                        mOwnerEnumerator.Parent.Property.Value.OnChildValueChanged(this);
                }

                if ((mOwnerEnumerator.Children.Count > 0) && (updateSource != ValueUpdateSource.FromChild) &&
                    (NoLinkWithChildren == false))
                {
                    // Inform its children that this value has changed
                    PropertyEnumerator childEnum = mOwnerEnumerator.Children;
                    while (childEnum != childEnum.RightBound)
                    {
                        if (childEnum.Property.Value != null)
                            childEnum.Property.Value.OnParentValueChanged(this);
                        childEnum.MoveNext();
                    }

                    ReUpdateFromChildren();
                }

                mOwnerEnumerator.Property.ParentGrid.Refresh();
            }

            return result;
        }

        protected internal virtual PropertyValue[] GetChildValues()
        {
            PropertyValue[] valueArray;

            PropertyTypeDescriptorContext context = GetTypeDescriptorContext(OwnerEnumerator);
            if (TypeConverter.GetPropertiesSupported(context))
            {
                PropertyDescriptorCollection collection = null;

                try
                {
                    collection = TypeConverter.GetProperties(context, UnderlyingValue,
                    new Attribute[1] { BrowsableAttribute.Yes });
                }
                catch (Exception)
                {
                }

                if (collection == null)
                    return null;

                // TODO : sort collection

                // TODO : gerer les arrays

                bool readOnlyProperties = TypeConverter.GetCreateInstanceSupported(null);
                valueArray = new PropertyValue[collection.Count];
                int index = 0;
                foreach (PropertyDescriptor descriptor in collection)
                {
                    PropertyValue propValue;
                    try
                    {
                        object obj = UnderlyingValue;
                        if (obj is ICustomTypeDescriptor)
                            obj = ((ICustomTypeDescriptor)obj).GetPropertyOwner(descriptor);

                        // TODO : pourquoi cette ligne ?
                        descriptor.GetValue(obj);
                    }
                    catch (Exception)
                    {
                        // Reason : activeXHide ?
                    }

                    if (readOnlyProperties)
                    {
                        propValue = new PropertyValueSimulated(Grid, this, descriptor);
                    }
                    else
                    {
                        // TODO : est-ce qu'on doit passer le parent value dans ce cas aussi ?
                        propValue = new PropertyValueIndirect(Grid, UnderlyingValue, descriptor, null);
                    }

                    PropertyIdAttribute attr = descriptor.Attributes[typeof(PropertyIdAttribute)] as PropertyIdAttribute;
                    propValue.Id = (attr != null ? attr.Id : 0);

                    valueArray[index++] = propValue;
                }
            }
            else
            {
                PropertyDescriptorCollection collection = TypeDescriptor.GetProperties(UnderlyingValue, new Attribute[1] { BrowsableAttribute.Yes });

                if (collection == null)
                    return null;

                if (Grid.PropertyComparer != null)
                    collection = collection.Sort(Grid.PropertyComparer);

                valueArray = new PropertyValue[collection.Count];
                int index = 0;
                foreach (PropertyDescriptor descriptor in collection)
                {
                    try
                    {
                        valueArray[index] = new PropertyValueIndirect(Grid, UnderlyingValue, descriptor, null);

                        PropertyIdAttribute attr = descriptor.Attributes[typeof(PropertyIdAttribute)] as PropertyIdAttribute;
                        valueArray[index].Id = (attr != null ? attr.Id : 0);

                        index++;
                    }
                    catch (Exception)
                    {
                    }
                }

                if (index != collection.Count)
                {
                    PropertyValue[] valueArray2 = new PropertyValue[index];
                    Array.Copy(valueArray, valueArray2, index);
                    valueArray = valueArray2;
                }
            }

            return valueArray;
        }

        /// <exclude />
        public enum ValueUpdateSource
        {
            FromChild,
            FromParent,
            FromOutside
        }

        /// <summary>
        /// Modifies the content of the underlying value.
        /// </summary>
        /// <param name="value">The object from which content must be assigned to the underlying value.</param>
        /// <param name="updateSource"></param>
        /// <returns>A boolean is the underlying value was successfully changed, false otherwise.</returns>
        protected virtual bool SetValueInternal(object value, ValueUpdateSource updateSource)
        {
            return false;
        }

        public string DisplayString
        {
            get
            {
                if (_look != null)
                    return _look.DisplayString;
                else
                    return GetDisplayString(GetStringValue());
            }
        }

        public string DisplayStringWithoutLook
        {
            get
            {
                return GetDisplayString(GetStringValue());
            }
        }

        public virtual string GetDisplayString(string actualStr)
        {
            return null;
        }

        public string GetActualString(string displayStr)
        {
            if (mDisplayedStrToActualStr != null)
            {
                // We try to find an actual string for the displayed string
                string actualStr = (string)mDisplayedStrToActualStr[displayStr];

                // But if we can't, we assume that maybe the parameter displayStr was actually an actual string
                return (actualStr == null ? displayStr : actualStr);
            }
            else
                return displayStr;
        }

        public virtual string GetStringValue()
        {
            return "";
        }

        public virtual Rectangle GetStringValueRect(Graphics graphics, Rectangle valueRect, Point point)
        {
	        if (_look != null)
		        return _look.GetDisplayStringRect(graphics, valueRect, point);
	        else
	        {
                Size size;
                if (HasMultipleValues)
                    size = Win32Calls.GetTextExtent(graphics, "", Font);
                else
                    size = Win32Calls.GetTextExtent(graphics, DisplayString, Font);

                Rectangle rect = valueRect;
                rect.X += PropertyGrid.GlobalTextMargin;
                rect.Width = Size.Round(size).Width;

                if ((ImageList != null) && (ImageIndex >= 0))
                {
                    if (_imageSize == Size.Empty)
                        rect.X += valueRect.Height + PropertyGrid.GlobalTextMargin;
                    else
                        rect.X += _imageSize.Width + PropertyGrid.GlobalTextMargin;
                }

                PropertyTypeDescriptorContext context = mOwnerEnumerator.Property.Value.GetTypeDescriptorContext(mOwnerEnumerator);
                if (DrawingDoneByEditor ||
                    ((Feel is PropertyUITypeEditorFeel) && (_uiTypeEditor.GetPaintValueSupported(context))))
                {
                    int delta = UITypeEditorCustomDrawing + 1 + PropertyGrid.GlobalTextMargin / 2;
                    rect.X += delta;
                    rect.Width = Size.Round(size).Width;
                }

                return rect;
	        }
        }

        public void DrawValue(Graphics graphics, Rectangle valueRect, Color textColor, PropertyEnumerator propEnum, string drawAnotherString)
        {
            if (Rectangle.Intersect(valueRect, Rectangle.Round(graphics.ClipBounds)).IsEmpty)
                return;

            if (_look != null)
                _look.OnDraw(graphics, valueRect, textColor, propEnum, drawAnotherString);
            else
                OnDraw(graphics, valueRect, textColor, propEnum, drawAnotherString);
        }

        private bool _initDrawingDoneByEditorForReadOnly = true;

        public virtual void OnDraw(Graphics graphics, Rectangle valueRect, Color textColor, PropertyEnumerator propEnum, string drawAnotherString)
        {
            string stringToDraw = "";
            if (drawAnotherString != null)
                stringToDraw = drawAnotherString;
            else if (HasMultipleValues == false)
                stringToDraw = DisplayString;

            PropertyTypeDescriptorContext context = propEnum.Property.Value.GetTypeDescriptorContext(propEnum);

            if (_initDrawingDoneByEditorForReadOnly)
            {
                if ((Feel == null) && (DrawingDoneByEditor == false))
                {
                    if (ReadOnly && (Grid.ReadOnlyVisual == PropertyGrid.ReadOnlyVisuals.Disabled) &&
                        (Grid.Grid.FindFeel(propEnum.Parent, propEnum) is PropertyUITypeEditorFeel))
                    {
                        if (_uiTypeEditor.GetPaintValueSupported(context))
                            _drawingDoneByEditor = true;
                    }
                }

                _initDrawingDoneByEditorForReadOnly = false;
            }

            if (((Feel is PropertyUITypeEditorFeel) && (_uiTypeEditor.GetPaintValueSupported(context))) ||
                  DrawingDoneByEditor)
            {
                Rectangle drawRect = valueRect;
                drawRect.X += PropertyGrid.GlobalTextMargin / 2;
                drawRect.Width = UITypeEditorCustomDrawing;
                drawRect.Y++;
                drawRect.Height -= 2;
                if (drawAnotherString == null)
                {
                    if (HasMultipleValues)
                        _uiTypeEditor.PaintValue(new PaintValueEventArgs(context, null, graphics, drawRect));
                    else
                        _uiTypeEditor.PaintValue(new PaintValueEventArgs(context,
                            propEnum.Property.Value.GetValue(), graphics, drawRect));
                }
                else
                {
                    _uiTypeEditor.PaintValue(new PaintValueEventArgs(context,
                        propEnum.Property.Value.GetActualValue(stringToDraw), graphics, drawRect));
                }

                graphics.DrawRectangle(SystemPens.WindowText, drawRect.Left, drawRect.Top, UITypeEditorCustomDrawing - 1, drawRect.Height - 1);

                drawRect.Height++;
                drawRect.X = drawRect.Right + PropertyGrid.GlobalTextMargin + 1;
                drawRect.Width = valueRect.Right - drawRect.Left;

                Win32Calls.DrawText(graphics, stringToDraw, ref drawRect, Font, textColor, Win32Calls.DT_VCENTER |
                    Win32Calls.DT_SINGLELINE | Win32Calls.DT_NOPREFIX |
                    ((mPropCtrl.EllipsisMode & PropertyGrid.EllipsisModes.EllipsisOnValues) != 0 ? Win32Calls.DT_END_ELLIPSIS : 0));

                return;
            }

            valueRect.Height++;

            Rectangle strRect = GetStringValueRect(graphics, valueRect, Point.Empty /* not used */);
            strRect.Width = valueRect.Right - strRect.Left;

            if (stringToDraw != null)
            {
                Font font;
                string hyperlink = mOwnerEnumerator.Property.HyperLink;
                if (hyperlink.Length != 0)
                {
                    font = new Font(Font, FontStyle.Underline);
                    textColor = Color.Blue;
                }
                else
                    font = Font;

                // Draw a bitmap if needed
                if ((ImageList != null) && (ImageIndex < ImageList.Images.Count) && (ImageIndex >= 0))
                {
                    Image img = ImageList.Images[ImageIndex];
                    if ((_imageSize != Size.Empty) && (_imageSize.Height <= valueRect.Height - 3))
                    {
                        int y = valueRect.Top + (valueRect.Height - 3 - _imageSize.Height) / 2 + 1;
                        graphics.DrawImage(img, valueRect.Left + PropertyGrid.GlobalTextMargin, y,
                            _imageSize.Width, _imageSize.Height);
                    }
                    else
                    {
                        graphics.DrawImage(img, valueRect.Left + PropertyGrid.GlobalTextMargin, valueRect.Top + 1,
                            valueRect.Height - 3, valueRect.Height - 3);
                    }
                }

                Win32Calls.DrawText(graphics, stringToDraw, ref strRect, font, textColor, Win32Calls.DT_VCENTER |
                    Win32Calls.DT_SINGLELINE | Win32Calls.DT_NOPREFIX |
                    ((mPropCtrl.EllipsisMode & PropertyGrid.EllipsisModes.EllipsisOnValues) != 0 ? Win32Calls.DT_END_ELLIPSIS : 0));

                if (hyperlink.Length != 0)
                    font.Dispose();
            }
        }

        protected TypeConverter mConverter;

        public virtual TypeConverter TypeConverter
        {
            get
            {
                if (mConverter == null)
                {
                    object obj = UnderlyingValue;
                    if (obj == null)
                        mConverter = TypeDescriptor.GetConverter(UnderlyingType);
                    else
                        mConverter = TypeDescriptor.GetConverter(obj);
                }

                return mConverter;
            }
        }

        protected string[] cachedDisplayedValues = null;

        public string[] GetDisplayedValues()
        {
            if ((cachedDisplayedValues == null) || (cachedDisplayedValues.Length == 0))
                ExtractDisplayedValues();

            return cachedDisplayedValues;
        }

        private string[] _standardValues = null;

        public virtual Attribute GetAttribute(Type attributeType)
        {
            List<Attribute> attrList;
            _customAttributes.TryGetValue(attributeType, out attrList);
            if (attrList != null)
                return attrList[0];

            return null;
        }

        public virtual List<Attribute> GetAttributes(Type attributeType)
        {
            List<Attribute> attrList;
            _customAttributes.TryGetValue(attributeType, out attrList);
            if (attrList != null)
                return attrList;

            return new List<Attribute>();
        }

        protected void ClearStandardValues()
        {
            _standardValues = null;
        }

        public string[] GetStandardValues()
        {
            if (_standardValues == null)
            {
                TypeConverter converter = TypeConverter;
                PropertyTypeDescriptorContext context = GetTypeDescriptorContext(OwnerEnumerator);

                if (UnderlyingType.IsEnum)
                {
                    ICollection collection = converter.GetStandardValues(context);
                    if (collection != null)
                    {
                        int i = 0;
                        _standardValues = new string[collection.Count];
                        foreach (object o in collection)
                            _standardValues[i++] = converter.ConvertToString(context, CultureInfo, o);
                    }
                    else
                        _standardValues = new string[0];
                }
                else
                {
                    ICollection collection = converter.GetStandardValues(context);
                    if (collection != null)
                    {
                        int i = 0;
                        _standardValues = new string[collection.Count];
                        foreach (object o in collection)
                            _standardValues[i++] = converter.ConvertToString(context, CultureInfo, o);
                    }
                    else
                        _standardValues = new string[0];
                }
            }

            return _standardValues;
        }

        protected Hashtable mActualStrToDisplayedStr;
        protected Hashtable mDisplayedStrToActualStr;
/*
        protected void ExtractDisplayedValues()
        {
            if (mActualStrToDisplayedStr != null)
            {
                mActualStrToDisplayedStr.Clear();
                mDisplayedStrToActualStr.Clear();
                cachedDisplayedValues = null;
            }

            ArrayList cachedDisplayedValueList = new ArrayList();

            // Try to find a PropertyDisplayedAs attribute on the property itself
            PropertyValueDisplayedAsAttribute pvdaAttr = null;
            List<Attribute> pvdaAttrs = GetAttributes(typeof(PropertyValueDisplayedAsAttribute));
            foreach (PropertyValueDisplayedAsAttribute attr in pvdaAttrs)
            {
                if (attr.DisplayedStrings != null)
                    pvdaAttr = attr;
            }

            if (pvdaAttr != null)
            {
                if (mActualStrToDisplayedStr == null)
                {
                    mActualStrToDisplayedStr = new Hashtable();
                    mDisplayedStrToActualStr = new Hashtable();
                }

                if (UnderlyingType.IsEnum)
                {
                    int i = 0;

                    FieldInfo[] fis = UnderlyingType.GetFields(BindingFlags.Public | BindingFlags.Static);
                    List<FieldInfo> fisList = new List<FieldInfo>(fis);
                    fisList.Sort(new FieldInfoEnumComparer(UnderlyingType));

                    foreach (FieldInfo fi in fisList)
                    {
                        bool done = false;

                        object[] attrs = fi.GetCustomAttributes(typeof(BrowsableAttribute), false);
                        if ((attrs.Length > 0) && (((BrowsableAttribute)attrs[0]).Browsable == false))
                        {
                            done = true;
                        }

                        if (done == false)
                        {
                            mActualStrToDisplayedStr[fi.Name] = pvdaAttr.DisplayedStrings[i];
                            mDisplayedStrToActualStr[pvdaAttr.DisplayedStrings[i]] = fi.Name;
                            cachedDisplayedValueList.Add(pvdaAttr.DisplayedStrings[i]);
                        }

                        i++;
                    }
                }
                else if (GetStandardValues().Length > 0)
                {
                    int i = 0;
                    foreach (string stdValue in GetStandardValues())
                    {
                        mActualStrToDisplayedStr[stdValue] = pvdaAttr.DisplayedStrings[i];
                        mDisplayedStrToActualStr[pvdaAttr.DisplayedStrings[i]] = stdValue;
                        cachedDisplayedValueList.Add(pvdaAttr.DisplayedStrings[i]);

                        i++;
                    }
                }
                else
                {
                    foreach (string displayedValue in pvdaAttr.DisplayedStrings)
                    {
                        mActualStrToDisplayedStr[displayedValue] = displayedValue;
                        mDisplayedStrToActualStr[displayedValue] = displayedValue;
                        cachedDisplayedValueList.Add(displayedValue);
                    }
                }
            }
            else if (ParentValue != null)
            {
                string[] displayedStrings = null;
                pvdaAttrs = ParentValue.GetAttributes(typeof(PropertyValueDisplayedAsAttribute));
                foreach (PropertyValueDisplayedAsAttribute attr in pvdaAttrs)
                {
                    displayedStrings = attr.GetDisplayedStrings(DisplayName);
                    if (displayedStrings != null)
                    {
                        pvdaAttr = attr;
                        break;
                    }
                }

                if (displayedStrings != null)
                {
                    if (mActualStrToDisplayedStr == null)
                    {
                        mActualStrToDisplayedStr = new Hashtable();
                        mDisplayedStrToActualStr = new Hashtable();
                    }

                    int i = 0;
                    foreach (string stdValue in GetStandardValues())
                    {
                        mActualStrToDisplayedStr[stdValue] = displayedStrings[i];
                        mDisplayedStrToActualStr[displayedStrings[i]] = stdValue;
                        cachedDisplayedValueList.Add(displayedStrings[i]);
                        i++;
                    }
                }
                else
                    pvdaAttr = null;
            }

            if (pvdaAttr == null)
            {
                object[] collection = GetStandardValues();

                if (collection.Length > 0)
                {
                    if (mActualStrToDisplayedStr == null)
                    {
                        mActualStrToDisplayedStr = new Hashtable();
                        mDisplayedStrToActualStr = new Hashtable();
                    }

                    if (UnderlyingType.IsEnum)
                    {
                        FieldInfo[] fis = UnderlyingType.GetFields(BindingFlags.Public | BindingFlags.Static);
                        List<FieldInfo> fisList = new List<FieldInfo>(fis);
                        fisList.Sort(new FieldInfoEnumComparer(UnderlyingType));

                        IEnumerator stdValuesEnumerator = collection.GetEnumerator();
                        stdValuesEnumerator.MoveNext();
                        foreach (FieldInfo fi in fisList)
                        {
                            string stdValue = stdValuesEnumerator.Current as string;

                            // Some TypeConverters remove some values in the enum so we must check
                            if (fi.Name != stdValue)
                                continue;

                            // If the field is not browsable, we continue to the next one
                            object[] attrs = fi.GetCustomAttributes(typeof(BrowsableAttribute), false);
                            if ((attrs.Length > 0) && (((BrowsableAttribute)attrs[0]).Browsable == false))
                            {
                                stdValuesEnumerator.MoveNext();
                                continue;
                            }

                            attrs = fi.GetCustomAttributes(typeof(PropertyValueDisplayedAsAttribute), false);
                            if (attrs.Length > 0)
                            {
                                string displayStr = ((PropertyValueDisplayedAsAttribute)attrs[0]).DisplayedStrings[0];
                                mDisplayedStrToActualStr[displayStr] = stdValue;
                                mActualStrToDisplayedStr[stdValue] = displayStr;
                                cachedDisplayedValueList.Add(displayStr);
                            }
                            else
                            {
                                cachedDisplayedValueList.Add(stdValue);
                                mActualStrToDisplayedStr[stdValue] = stdValue;
                                mDisplayedStrToActualStr[stdValue] = stdValue;
                            }

                            if (stdValuesEnumerator.MoveNext() == false)
                                break;
                        }
                    }
                    else
                    {
                        foreach (string stdValue in collection)
                        {
                            cachedDisplayedValueList.Add(stdValue);
                            mActualStrToDisplayedStr[stdValue] = stdValue;
                            mDisplayedStrToActualStr[stdValue] = stdValue;
                        }
                    }
                }
            }

            cachedDisplayedValues = new string[cachedDisplayedValueList.Count];
            cachedDisplayedValueList.CopyTo(cachedDisplayedValues, 0);
        }
*/
        protected void ExtractDisplayedValues()
        {
            ExtractDisplayedValues(false);
        }

        protected void ExtractDisplayedValues(bool dontTriggerEvent)
        {
            if (mActualStrToDisplayedStr != null)
            {
                mActualStrToDisplayedStr.Clear();
                mDisplayedStrToActualStr.Clear();
                cachedDisplayedValues = null;
            }

            ArrayList cachedDisplayedValueList = new ArrayList();

            string[] displayedValues = new string[0];

            // 1. Try to find if there is a dynamic overriden by the client app at run-time
            if (dontTriggerEvent == false)
            {
                DisplayedValuesNeededEventArgs e = new DisplayedValuesNeededEventArgs(OwnerEnumerator);
                Grid.OnDisplayedValuesNeeded(e);
                if (e.DisplayedValues == null)
                    displayedValues = new string[0];
                else
                    displayedValues = e.DisplayedValues;
            }

            // 2. Try to find a PropertyDisplayedAs attribute on the property itself
            if (displayedValues.Length == 0)
            {
                List<Attribute> pvdaAttrs = GetAttributes(typeof(PropertyValueDisplayedAsAttribute));
                foreach (PropertyValueDisplayedAsAttribute attr in pvdaAttrs)
                {
                    if (attr.DisplayedStrings != null)
                    {
                        displayedValues = attr.DisplayedStrings;
                        break;
                    }
                }
            }

            // 3. Try to find a PropertyDisplayedAs attribute on the parent of the property
            if ((displayedValues.Length == 0) && (ParentValue != null))
            {
                List<Attribute>  pvdaAttrs = ParentValue.GetAttributes(typeof(PropertyValueDisplayedAsAttribute));
                foreach (PropertyValueDisplayedAsAttribute attr in pvdaAttrs)
                {
                    string[] displayedStrings = attr.GetDisplayedStrings(DisplayName);
                    if (displayedStrings != null)
                    {
                        displayedValues = displayedStrings;
                        break;
                    }
                }
            }

            if (displayedValues.Length != 0)
            {
                if (mActualStrToDisplayedStr == null)
                {
                    mActualStrToDisplayedStr = new Hashtable();
                    mDisplayedStrToActualStr = new Hashtable();
                }

                if (UnderlyingType.IsEnum)
                {
                    int i = 0;

                    FieldInfo[] fis = UnderlyingType.GetFields(BindingFlags.Public | BindingFlags.Static);
                    List<FieldInfo> fisList = new List<FieldInfo>(fis);
                    fisList.Sort(new FieldInfoEnumComparer(UnderlyingType));

                    foreach (FieldInfo fi in fisList)
                    {
                        bool done = false;

                        object[] attrs = fi.GetCustomAttributes(typeof(BrowsableAttribute), false);
                        if ((attrs.Length > 0) && (((BrowsableAttribute)attrs[0]).Browsable == false))
                        {
                            done = true;
                        }

                        if (done == false)
                        {
                            mActualStrToDisplayedStr[fi.Name] = displayedValues[i];
                            mDisplayedStrToActualStr[displayedValues[i]] = fi.Name;
                            cachedDisplayedValueList.Add(displayedValues[i]);
                        }

                        i++;
                    }
                }
                else if (GetStandardValues().Length > 0)
                {
                    int i = 0;
                    foreach (string stdValue in GetStandardValues())
                    {
                        mActualStrToDisplayedStr[stdValue] = displayedValues[i];
                        mDisplayedStrToActualStr[displayedValues[i]] = stdValue;
                        cachedDisplayedValueList.Add(displayedValues[i]);

                        i++;
                    }
                }
                else
                {
                    foreach (string displayedValue in displayedValues)
                    {
                        mActualStrToDisplayedStr[displayedValue] = displayedValue;
                        mDisplayedStrToActualStr[displayedValue] = displayedValue;
                        cachedDisplayedValueList.Add(displayedValue);
                    }
                }
            }
            else
            {
                object[] collection = GetStandardValues();

                if (collection.Length > 0)
                {
                    if (mActualStrToDisplayedStr == null)
                    {
                        mActualStrToDisplayedStr = new Hashtable();
                        mDisplayedStrToActualStr = new Hashtable();
                    }

                    if (UnderlyingType.IsEnum)
                    {
                        FieldInfo[] fis = UnderlyingType.GetFields(BindingFlags.Public | BindingFlags.Static);
                        List<FieldInfo> fisList = new List<FieldInfo>(fis);
                        fisList.Sort(new FieldInfoEnumComparer(UnderlyingType));

                        IEnumerator stdValuesEnumerator = collection.GetEnumerator();
                        stdValuesEnumerator.MoveNext();
                        foreach (FieldInfo fi in fisList)
                        {
                            string stdValue = stdValuesEnumerator.Current as string;

                            // Some TypeConverters remove some values in the enum so we must check
							PropertyTypeDescriptorContext context = GetTypeDescriptorContext(OwnerEnumerator);
							if (fi.Name != TypeConverter.ConvertFromString(context, CultureInfo, stdValue).ToString())
								continue;


                            // If the field is not browsable, we continue to the next one
                            object[] attrs = fi.GetCustomAttributes(typeof(BrowsableAttribute), false);
                            if ((attrs.Length > 0) && (((BrowsableAttribute)attrs[0]).Browsable == false))
                            {
                                stdValuesEnumerator.MoveNext();
                                continue;
                            }

                            attrs = fi.GetCustomAttributes(typeof(PropertyValueDisplayedAsAttribute), false);
                            if (attrs.Length > 0)
                            {
                                string displayStr = ((PropertyValueDisplayedAsAttribute)attrs[0]).DisplayedStrings[0];
                                mDisplayedStrToActualStr[displayStr] = stdValue;
                                mActualStrToDisplayedStr[stdValue] = displayStr;
                                cachedDisplayedValueList.Add(displayStr);
                            }
                            else
                            {
                                cachedDisplayedValueList.Add(stdValue);
                                mActualStrToDisplayedStr[stdValue] = stdValue;
                                mDisplayedStrToActualStr[stdValue] = stdValue;
                            }

                            if (stdValuesEnumerator.MoveNext() == false)
                                break;
                        }
                    }
                    else
                    {
                        foreach (string stdValue in collection)
                        {
                            cachedDisplayedValueList.Add(stdValue);
                            mActualStrToDisplayedStr[stdValue] = stdValue;
                            mDisplayedStrToActualStr[stdValue] = stdValue;
                        }
                    }
                }
            }

            cachedDisplayedValues = new string[cachedDisplayedValueList.Count];
            cachedDisplayedValueList.CopyTo(cachedDisplayedValues, 0);
        }

        /// <summary>
        /// Clears any previously associated set of displayed strings for this value and reassociates a
        /// new set of strings.
        /// </summary>
        /// <param name="displayedValues">The array of new displayed strings for this value.</param>
        public void ResetDisplayedValues(string[] displayedValues)
        {
            PropertyValueDisplayedAsAttribute attr = new PropertyValueDisplayedAsAttribute(displayedValues);
            SetSingleAttribute(attr);
            ClearStandardValues();

            ExtractDisplayedValues(true);
        }

        /// <summary>
        /// Clears any previously associated set of displayed strings for this value.
        /// </summary>
        /// <remarks>
        /// If you don't further associate new displayed strings, the value will use the standard values.
        /// For example, an enumeration will use the strings provided in the enumeration fields.
        /// </remarks>
        public void ResetDisplayedValues(bool triggerEvent)
        {
            DeleteSingleAttribute(new PropertyValueDisplayedAsAttribute());

            ClearStandardValues();

            if (triggerEvent)
                ExtractDisplayedValues();
            else
            {
                if (mActualStrToDisplayedStr != null)
                {
                    mActualStrToDisplayedStr.Clear();
                    mDisplayedStrToActualStr.Clear();
                    cachedDisplayedValues = null;
                }
            }
        }

        public object GetValue()
        {
            return UnderlyingValue;
        }

        public object GetValueToValidate(string valueToValidateAsStr)
        {
            return GetActualValue(valueToValidateAsStr);
        }

        public object GetActualValue(string drawAnotherString)
        {
            try
            {
                PropertyTypeDescriptorContext context = GetTypeDescriptorContext(OwnerEnumerator);
                if (TypeConverter.CanConvertFrom(context, typeof(string)))
                {
                    return TypeConverter.ConvertFromString(context, CultureInfo,
                    (drawAnotherString == null ? DisplayString : drawAnotherString));
                }
                else if (drawAnotherString != null)
                    return drawAnotherString;
                else
                    return UnderlyingValue;
            }
            catch (Exception)
            {
                return UnderlyingValue;
            }
        }

        public virtual object TargetInstance
        {
            get
            {
                return null;
            }
        }

        public virtual PropertyDescriptor PropertyDescriptor
        {
            get
            {
                return null;
            }
        }
        
        public bool ReadOnlyByTypeConverter
        {
            get
            {
                if (TypeConverter != null)
                {
                    PropertyTypeDescriptorContext context = GetTypeDescriptorContext(mOwnerEnumerator);
                    if (TypeConverter.CanConvertFrom(context, typeof(string)) == false)
                        return true;

                    if (TypeConverter.GetStandardValuesExclusive(context))
                        return true;
                }

                // TODO : on pourrait inventer un attribute [ReadOnlyTextbox] qui permettrait d'avoir juste
                // le textbox readonly mais pas le listbox par exemple. Ca eviterait d'avoir a ecrire un
                // TypeConverter juste pour qu'il ramene GetStandardValuesExclusive()==true.
                
                return false;
            }
        }
        
        public virtual bool IsReadOnly(PropertyEnumerator selfEnum)
        {
            return false;
        }

        public virtual string GetTooltipString(int line)
        {
	        if (Look != null)
                return Look.GetTooltipString(line);
	        else
                return GetDisplayString(GetStringValue());
        }

        public virtual Size GetTooltipStringSize(Graphics graphics, Rectangle valueRect, Point point, int line)
        {
            if (Look != null)
                return Look.GetTooltipStringSize(graphics, valueRect, point, line);
            else
                return Win32Calls.GetTextExtent(graphics, DisplayString, Font);
        }

        public virtual PropertyTypeDescriptorContext GetTypeDescriptorContext(PropertyEnumerator propEnum)
        {
            return null;
        }

        protected virtual void OnParentValueChanged(PropertyValue parentPropValue)
        {
        }

        protected internal virtual void OnChildValueChanged(PropertyValue parentPropValue)
        {
        }

        protected virtual void ReUpdateFromChildren()
        {
        }

        public virtual void Recreate()
        {
        }

        protected internal virtual void OnPropertyChanged(PropertyChangedEventArgs e)
        {
            if (PropertyChangedEvent != null)
                PropertyChangedEvent(this, e);
        }
    }
}
