Code:
/ 4.0 / 4.0 / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / fx / src / WinForms / Managed / System / WinForms / PropertyGridInternal / GridEntry.cs / 1305376 / GridEntry.cs
//------------------------------------------------------------------------------ //// Copyright (c) Microsoft Corporation. All rights reserved. // //----------------------------------------------------------------------------- //#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; ////// /// Base Entry for properties to be displayed in properties window. /// 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; 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; } } public AccessibleObject AccessibilityObject { get { if (accessibleObject == null) { accessibleObject = new GridEntryAccessibleObject(this); } return accessibleObject; } } ////// /// specify that this grid entry should be allowed to be merged for. /// multi-select. /// public virtual bool AllowMerge { get { return true; } } internal virtual bool AlwaysAllowExpand { get { return false; } } internal virtual AttributeCollection Attributes { get { return TypeDescriptor.GetAttributes(PropertyType); } } ////// /// 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. /// protected virtual Brush GetBackgroundBrush(Graphics g) { return GridEntryHost.GetBackgroundBrush(g); } ////// /// protected virtual Color LabelTextColor { get { if (this.ShouldRenderReadOnly) { return GridEntryHost.GrayTextColor; } else { return GridEntryHost.GetTextColor(); } } } ////// /// The set of attributes that will be used for browse filtering /// public virtual AttributeCollection BrowsableAttributes { get{ if (parentPE != null) { return parentPE.BrowsableAttributes; } return null; } set{ parentPE.BrowsableAttributes = value; } } ////// /// 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. /// 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; } } ////// /// 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. /// 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; } } } ////// /// Returns the default child GridEntry of this item. Usually the default property /// of the target object. /// 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); } } } } 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; } } ////// /// Checks if the entry is currently expanded /// 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); } } } } } ////// /// 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" /// 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; } } ////// /// Returns true if this GridEntry has a value field in the right hand column. /// internal virtual bool HasValue { get { return true; } } ////// /// Retrieves the keyword that the VS help dynamic help window will /// use when this IPE is selected. /// 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)); } } ////// /// 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. /// 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; } } ////// /// Retrieves the PropertyDescriptor that is surfacing the given object/ /// public override PropertyDescriptor PropertyDescriptor { get { return null; } } ////// /// Returns the pixel indent of the current GridEntry's label. /// 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; } } ////// /// Returns rect that the outline icon (+ or - or arrow) will be drawn into, relative /// to the upper left corner of the GridEntry. /// 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 + OUTLINE_ICON_PADDING; int left = (propertyDepth * borderWidth) + (OUTLINE_ICON_PADDING) / 2;//+ 1; int top = (gridHost.GetGridEntryHeight() - outlineSize) / 2;// - 1; // figure out edit positioning. outlineRect = new Rectangle(left, top, outlineSize, outlineSize); return outlineRect; } } 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; // [....], 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; } } ////// /// Returns category name of the current property /// public virtual string PropertyCategory { get { return CategoryAttribute.Default.Category; } } ////// /// 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. /// public virtual int PropertyDepth { get { return propertyDepth; } } ////// /// Returns the description helpstring for this GridEntry. /// public virtual string PropertyDescription { get { return null; } } ////// /// Returns the label of this property. Usually /// this is the property name. /// public virtual string PropertyLabel { get { return null; } } ////// /// Returns non-localized name of this property. /// public virtual string PropertyName { get { return this.PropertyLabel; } } ////// /// Returns the Type of the value of this GridEntry, or null if the value is null. /// public virtual Type PropertyType { get { object obj = this.PropertyValue; if (obj != null) { return obj.GetType(); } else { return null; } } } ////// /// Gets or sets the value for the property that is represented /// by this GridEntry. /// 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)))); } } ////// /// Returns the type converter for this entry. /// 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; } } ////// /// Returns the type editor for this entry. This may return null if there /// is no type editor. /// 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; } } ////// /// Add an event handler to be invoked when the label portion of /// the prop entry is clicked /// public virtual void AddOnLabelClick(EventHandler h) { AddEventHandler(EVENT_LABEL_CLICK, h); } ////// /// Add an event handler to be invoked when the label portion of /// the prop entry is double /// public virtual void AddOnLabelDoubleClick(EventHandler h) { AddEventHandler(EVENT_LABEL_DBLCLICK, h); } ////// /// Add an event handler to be invoked when the value portion of /// the prop entry is clicked /// public virtual void AddOnValueClick(EventHandler h) { AddEventHandler(EVENT_VALUE_CLICK, h); } ////// /// Add an event handler to be invoked when the value portion of /// the prop entry is double-clicked /// public virtual void AddOnValueDoubleClick(EventHandler h) { AddEventHandler(EVENT_VALUE_DBLCLICK, h); } ////// /// Add an event handler to be invoked when the outline icone portion of /// the prop entry is clicked /// public virtual void AddOnOutlineClick(EventHandler h) { AddEventHandler(EVENT_OUTLINE_CLICK, h); } ////// /// Add an event handler to be invoked when the outline icone portion of /// the prop entry is double clicked /// public virtual void AddOnOutlineDoubleClick(EventHandler h) { AddEventHandler(EVENT_OUTLINE_DBLCLICK, h); } ////// /// Add an event handler to be invoked when the children grid entries are re-created. /// 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(); } } } ////// /// Converts the given string of text to a value. /// public object ConvertTextToValue(string text) { if (TypeConverter.CanConvertFrom(this, typeof(string))) { return TypeConverter.ConvertFromString(this, text); } return text; } ////// /// Create the base prop entries given an object or set of objects /// 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; } ////// /// Populates the children of this grid entry /// protected virtual bool CreateChildren() { return CreateChildren(false); } ////// /// Populates the children of this grid entry /// 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(); } } ////// /// Disposes the array of children /// public virtual void DisposeChildren() { if (childCollection != null) { childCollection.Dispose(); childCollection = null; } } ~GridEntry() { Dispose(false); } ////// /// Invokes the type editor for editing this item. /// 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); } } } } ////// /// Tests two GridEntries for equality /// public override bool Equals(object obj) { if (NonParentEquals(obj)) { return((GridEntry)obj).ParentGridEntry == this.ParentGridEntry; } return false; } ////// /// Searches for a value of a given property for a value editor user /// 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; } ////// /// Returns the index of a child GridEntry /// internal virtual int GetChildIndex(GridEntry pe) { return this.Children.GetEntry(pe); } ////// /// 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 /// 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; } ////// /// Gets the owner of the current value. This is usually the value of the /// root entry, which is the object being browsed /// public virtual object GetValueOwner() { if (parentPE == null) { return this.PropertyValue; } return parentPE.GetChildValueOwner(this); } ////// /// 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 /// public virtual object[] GetValueOwners() { object owner = GetValueOwner(); if (owner != null) { return new object[] { owner}; } return null; } ////// /// Gets the owner of the current value. This is usually the value of the /// root entry, which is the object being browsed /// 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; } ////// /// Returns a string with info about the currently selected GridEntry /// 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; } ////// /// Retrieves the type of the value for this GridEntry /// public virtual Type GetValueType() { return this.PropertyType; } ////// /// Returns the child GridEntries for this item. /// 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; } ////// /// Resets the current item /// public virtual void ResetPropertyValue() { NotifyValue(NOTIFY_RESET); Refresh(); } /* ////// Checks if the value of the current item can be modified by the user. /// ////// True if the value can be modified /// public virtual bool CanSetPropertyValue() { return 0 != (Flags & (GridEntry.FLAG_DROPDOWN_EDITABLE | GridEntry.FLAG_TEXT_EDITABLE | GridEntry.FLAG_CUSTOM_EDITABLE | GridEntry.FLAG_ENUMERABLE)); } */ /* ////// 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. /// ////// True if the value associated with this property (e.g. it's children) can be modified even if it's readonly. /// public virtual bool CanSetReadOnlyPropertyValue() { return GetFlagSet(GridEntry.FLAG_READONLY_EDITABLE); } */ ////// /// Returns if the property can be reset /// public virtual bool CanResetPropertyValue() { return NotifyValue(NOTIFY_CAN_RESET); } ////// /// Called when the item is double clicked. /// public virtual bool DoubleClickPropertyValue() { return NotifyValue(NOTIFY_DBL_CLICK); } ////// /// Returns the text value of this property. /// public virtual string GetPropertyTextValue() { return GetPropertyTextValue(this.PropertyValue); } ////// /// Returns the text value of this property. /// 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; } ////// /// Returns the text values of this property. /// 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))); } ////// /// Checks if a given flag is set /// 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(); } ////// /// Retrieves the requested service. This may /// return null if the requested service is not /// available. /// 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; } ////// /// 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. /// 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 ? SystemBrushes.Highlight : 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(SystemBrushes.Highlight, 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); // Do actual drawing // A brush is needed if using GDI+ only (UseCompatibleTextRendering); if using GDI, only the color is needed. Color textColor = (selected && hasFocus) ? SystemColors.HighlightText : 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); } ////// /// 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. /// 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); } // 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) { 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; VisualStyleRenderer explorerTreeRenderer = new VisualStyleRenderer(element); explorerTreeRenderer.DrawBackground(g, outline); } } 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(); if (penColor.IsSystemColor) { p = SystemPens.FromSystemColor(penColor); } else { p = new Pen(penColor); } g.FillRectangle(b, outline); 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(); } } } ////// /// 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. /// 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 default brush // Brush bkBrush = selected ? SystemBrushes.Highlight : this.BackgroundBrush; 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 = System.Drawing.SystemBrushes.Highlight; textColor = SystemColors.HighlightText; } 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 (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) ? SystemColors.Highlight : 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); } } ////// /// 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. /// protected virtual void OnLabelClick(EventArgs e) { RaiseEvent(EVENT_LABEL_CLICK, e); } ////// /// 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. /// protected virtual void OnLabelDoubleClick(EventArgs e) { RaiseEvent(EVENT_LABEL_DBLCLICK, e); } ////// /// Called when the GridEntry is clicked. /// 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; } ////// /// 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. /// protected virtual void OnOutlineClick(EventArgs e) { RaiseEvent(EVENT_OUTLINE_CLICK, e); } ////// /// 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. /// protected virtual void OnOutlineDoubleClick(EventArgs e) { RaiseEvent(EVENT_OUTLINE_DBLCLICK, e); } ////// /// 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. /// protected virtual void OnRecreateChildren(GridEntryRecreateChildrenEventArgs e) { Delegate handler = GetEventHandler(EVENT_RECREATE_CHILDREN); if (handler != null) ((GridEntryRecreateChildrenEventHandler)handler)(this, e); } ////// /// 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. /// protected virtual void OnValueClick(EventArgs e) { RaiseEvent(EVENT_VALUE_CLICK, e); } ////// /// 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. /// protected virtual void OnValueDoubleClick(EventArgs e) { RaiseEvent(EVENT_VALUE_DBLCLICK, e); } internal bool OnValueReturnKey() { return NotifyValue(NOTIFY_RETURN); } ////// /// Sets the specified flag /// protected virtual void SetFlag(int flag, bool fVal) { SetFlag(flag, (fVal ? flag : 0)); } ////// /// Sets the default child of this entry, given a valid value mask. /// protected virtual void SetFlag(int flag_valid, int flag, bool fVal) { SetFlag(flag_valid | flag, flag_valid | (fVal ? flag : 0)); } ////// /// Sets the value of a flag /// 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; } ////// /// Checks if this value should be persisited. /// 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; } ////// /// Sends a notify message to this GridEntry, and returns the success result /// internal virtual bool NotifyValueGivenParent(object obj, int type) { return false; } ////// /// Sends a notify message to the child GridEntry, and returns the success result /// internal virtual bool NotifyChildValue(GridEntry pe, int type) { return pe.NotifyValueGivenParent(pe.GetValueOwner(),type); } internal virtual bool NotifyValue(int type) { // KILLME, [....], 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)); } ////// /// Refresh the current GridEntry's value and it's children /// 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(); } ////// /// Sets the value of this GridEntry from text /// 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; } ////// /// 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 { GridEntry owner = null; private delegate void SelectDelegate(AccessibleSelection flags); 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; } } [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 { 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); } } ////// /// Returns the currently focused child, if any. /// Returns this if the object itself is focused. /// public override AccessibleObject GetFocused() { if (owner.Focus) { return this; } else { return null; } } ////// /// Navigate to the next or previous grid entry. /// [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); } } } public class DisplayNameSortComparer : IComparer { public int Compare(object left, object right) { // review: ([....]) 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; } } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. //------------------------------------------------------------------------------ //// Copyright (c) Microsoft Corporation. All rights reserved. // //----------------------------------------------------------------------------- //#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; ////// /// Base Entry for properties to be displayed in properties window. /// 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; 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; } } public AccessibleObject AccessibilityObject { get { if (accessibleObject == null) { accessibleObject = new GridEntryAccessibleObject(this); } return accessibleObject; } } ////// /// specify that this grid entry should be allowed to be merged for. /// multi-select. /// public virtual bool AllowMerge { get { return true; } } internal virtual bool AlwaysAllowExpand { get { return false; } } internal virtual AttributeCollection Attributes { get { return TypeDescriptor.GetAttributes(PropertyType); } } ////// /// 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. /// protected virtual Brush GetBackgroundBrush(Graphics g) { return GridEntryHost.GetBackgroundBrush(g); } ////// /// protected virtual Color LabelTextColor { get { if (this.ShouldRenderReadOnly) { return GridEntryHost.GrayTextColor; } else { return GridEntryHost.GetTextColor(); } } } ////// /// The set of attributes that will be used for browse filtering /// public virtual AttributeCollection BrowsableAttributes { get{ if (parentPE != null) { return parentPE.BrowsableAttributes; } return null; } set{ parentPE.BrowsableAttributes = value; } } ////// /// 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. /// 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; } } ////// /// 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. /// 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; } } } ////// /// Returns the default child GridEntry of this item. Usually the default property /// of the target object. /// 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); } } } } 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; } } ////// /// Checks if the entry is currently expanded /// 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); } } } } } ////// /// 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" /// 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; } } ////// /// Returns true if this GridEntry has a value field in the right hand column. /// internal virtual bool HasValue { get { return true; } } ////// /// Retrieves the keyword that the VS help dynamic help window will /// use when this IPE is selected. /// 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)); } } ////// /// 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. /// 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; } } ////// /// Retrieves the PropertyDescriptor that is surfacing the given object/ /// public override PropertyDescriptor PropertyDescriptor { get { return null; } } ////// /// Returns the pixel indent of the current GridEntry's label. /// 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; } } ////// /// Returns rect that the outline icon (+ or - or arrow) will be drawn into, relative /// to the upper left corner of the GridEntry. /// 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 + OUTLINE_ICON_PADDING; int left = (propertyDepth * borderWidth) + (OUTLINE_ICON_PADDING) / 2;//+ 1; int top = (gridHost.GetGridEntryHeight() - outlineSize) / 2;// - 1; // figure out edit positioning. outlineRect = new Rectangle(left, top, outlineSize, outlineSize); return outlineRect; } } 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; // [....], 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; } } ////// /// Returns category name of the current property /// public virtual string PropertyCategory { get { return CategoryAttribute.Default.Category; } } ////// /// 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. /// public virtual int PropertyDepth { get { return propertyDepth; } } ////// /// Returns the description helpstring for this GridEntry. /// public virtual string PropertyDescription { get { return null; } } ////// /// Returns the label of this property. Usually /// this is the property name. /// public virtual string PropertyLabel { get { return null; } } ////// /// Returns non-localized name of this property. /// public virtual string PropertyName { get { return this.PropertyLabel; } } ////// /// Returns the Type of the value of this GridEntry, or null if the value is null. /// public virtual Type PropertyType { get { object obj = this.PropertyValue; if (obj != null) { return obj.GetType(); } else { return null; } } } ////// /// Gets or sets the value for the property that is represented /// by this GridEntry. /// 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)))); } } ////// /// Returns the type converter for this entry. /// 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; } } ////// /// Returns the type editor for this entry. This may return null if there /// is no type editor. /// 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; } } ////// /// Add an event handler to be invoked when the label portion of /// the prop entry is clicked /// public virtual void AddOnLabelClick(EventHandler h) { AddEventHandler(EVENT_LABEL_CLICK, h); } ////// /// Add an event handler to be invoked when the label portion of /// the prop entry is double /// public virtual void AddOnLabelDoubleClick(EventHandler h) { AddEventHandler(EVENT_LABEL_DBLCLICK, h); } ////// /// Add an event handler to be invoked when the value portion of /// the prop entry is clicked /// public virtual void AddOnValueClick(EventHandler h) { AddEventHandler(EVENT_VALUE_CLICK, h); } ////// /// Add an event handler to be invoked when the value portion of /// the prop entry is double-clicked /// public virtual void AddOnValueDoubleClick(EventHandler h) { AddEventHandler(EVENT_VALUE_DBLCLICK, h); } ////// /// Add an event handler to be invoked when the outline icone portion of /// the prop entry is clicked /// public virtual void AddOnOutlineClick(EventHandler h) { AddEventHandler(EVENT_OUTLINE_CLICK, h); } ////// /// Add an event handler to be invoked when the outline icone portion of /// the prop entry is double clicked /// public virtual void AddOnOutlineDoubleClick(EventHandler h) { AddEventHandler(EVENT_OUTLINE_DBLCLICK, h); } ////// /// Add an event handler to be invoked when the children grid entries are re-created. /// 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(); } } } ////// /// Converts the given string of text to a value. /// public object ConvertTextToValue(string text) { if (TypeConverter.CanConvertFrom(this, typeof(string))) { return TypeConverter.ConvertFromString(this, text); } return text; } ////// /// Create the base prop entries given an object or set of objects /// 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; } ////// /// Populates the children of this grid entry /// protected virtual bool CreateChildren() { return CreateChildren(false); } ////// /// Populates the children of this grid entry /// 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(); } } ////// /// Disposes the array of children /// public virtual void DisposeChildren() { if (childCollection != null) { childCollection.Dispose(); childCollection = null; } } ~GridEntry() { Dispose(false); } ////// /// Invokes the type editor for editing this item. /// 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); } } } } ////// /// Tests two GridEntries for equality /// public override bool Equals(object obj) { if (NonParentEquals(obj)) { return((GridEntry)obj).ParentGridEntry == this.ParentGridEntry; } return false; } ////// /// Searches for a value of a given property for a value editor user /// 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; } ////// /// Returns the index of a child GridEntry /// internal virtual int GetChildIndex(GridEntry pe) { return this.Children.GetEntry(pe); } ////// /// 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 /// 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; } ////// /// Gets the owner of the current value. This is usually the value of the /// root entry, which is the object being browsed /// public virtual object GetValueOwner() { if (parentPE == null) { return this.PropertyValue; } return parentPE.GetChildValueOwner(this); } ////// /// 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 /// public virtual object[] GetValueOwners() { object owner = GetValueOwner(); if (owner != null) { return new object[] { owner}; } return null; } ////// /// Gets the owner of the current value. This is usually the value of the /// root entry, which is the object being browsed /// 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; } ////// /// Returns a string with info about the currently selected GridEntry /// 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; } ////// /// Retrieves the type of the value for this GridEntry /// public virtual Type GetValueType() { return this.PropertyType; } ////// /// Returns the child GridEntries for this item. /// 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; } ////// /// Resets the current item /// public virtual void ResetPropertyValue() { NotifyValue(NOTIFY_RESET); Refresh(); } /* ////// Checks if the value of the current item can be modified by the user. /// ////// True if the value can be modified /// public virtual bool CanSetPropertyValue() { return 0 != (Flags & (GridEntry.FLAG_DROPDOWN_EDITABLE | GridEntry.FLAG_TEXT_EDITABLE | GridEntry.FLAG_CUSTOM_EDITABLE | GridEntry.FLAG_ENUMERABLE)); } */ /* ////// 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. /// ////// True if the value associated with this property (e.g. it's children) can be modified even if it's readonly. /// public virtual bool CanSetReadOnlyPropertyValue() { return GetFlagSet(GridEntry.FLAG_READONLY_EDITABLE); } */ ////// /// Returns if the property can be reset /// public virtual bool CanResetPropertyValue() { return NotifyValue(NOTIFY_CAN_RESET); } ////// /// Called when the item is double clicked. /// public virtual bool DoubleClickPropertyValue() { return NotifyValue(NOTIFY_DBL_CLICK); } ////// /// Returns the text value of this property. /// public virtual string GetPropertyTextValue() { return GetPropertyTextValue(this.PropertyValue); } ////// /// Returns the text value of this property. /// 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; } ////// /// Returns the text values of this property. /// 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))); } ////// /// Checks if a given flag is set /// 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(); } ////// /// Retrieves the requested service. This may /// return null if the requested service is not /// available. /// 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; } ////// /// 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. /// 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 ? SystemBrushes.Highlight : 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(SystemBrushes.Highlight, 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); // Do actual drawing // A brush is needed if using GDI+ only (UseCompatibleTextRendering); if using GDI, only the color is needed. Color textColor = (selected && hasFocus) ? SystemColors.HighlightText : 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); } ////// /// 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. /// 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); } // 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) { 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; VisualStyleRenderer explorerTreeRenderer = new VisualStyleRenderer(element); explorerTreeRenderer.DrawBackground(g, outline); } } 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(); if (penColor.IsSystemColor) { p = SystemPens.FromSystemColor(penColor); } else { p = new Pen(penColor); } g.FillRectangle(b, outline); 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(); } } } ////// /// 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. /// 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 default brush // Brush bkBrush = selected ? SystemBrushes.Highlight : this.BackgroundBrush; 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 = System.Drawing.SystemBrushes.Highlight; textColor = SystemColors.HighlightText; } 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 (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) ? SystemColors.Highlight : 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); } } ////// /// 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. /// protected virtual void OnLabelClick(EventArgs e) { RaiseEvent(EVENT_LABEL_CLICK, e); } ////// /// 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. /// protected virtual void OnLabelDoubleClick(EventArgs e) { RaiseEvent(EVENT_LABEL_DBLCLICK, e); } ////// /// Called when the GridEntry is clicked. /// 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; } ////// /// 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. /// protected virtual void OnOutlineClick(EventArgs e) { RaiseEvent(EVENT_OUTLINE_CLICK, e); } ////// /// 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. /// protected virtual void OnOutlineDoubleClick(EventArgs e) { RaiseEvent(EVENT_OUTLINE_DBLCLICK, e); } ////// /// 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. /// protected virtual void OnRecreateChildren(GridEntryRecreateChildrenEventArgs e) { Delegate handler = GetEventHandler(EVENT_RECREATE_CHILDREN); if (handler != null) ((GridEntryRecreateChildrenEventHandler)handler)(this, e); } ////// /// 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. /// protected virtual void OnValueClick(EventArgs e) { RaiseEvent(EVENT_VALUE_CLICK, e); } ////// /// 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. /// protected virtual void OnValueDoubleClick(EventArgs e) { RaiseEvent(EVENT_VALUE_DBLCLICK, e); } internal bool OnValueReturnKey() { return NotifyValue(NOTIFY_RETURN); } ////// /// Sets the specified flag /// protected virtual void SetFlag(int flag, bool fVal) { SetFlag(flag, (fVal ? flag : 0)); } ////// /// Sets the default child of this entry, given a valid value mask. /// protected virtual void SetFlag(int flag_valid, int flag, bool fVal) { SetFlag(flag_valid | flag, flag_valid | (fVal ? flag : 0)); } ////// /// Sets the value of a flag /// 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; } ////// /// Checks if this value should be persisited. /// 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; } ////// /// Sends a notify message to this GridEntry, and returns the success result /// internal virtual bool NotifyValueGivenParent(object obj, int type) { return false; } ////// /// Sends a notify message to the child GridEntry, and returns the success result /// internal virtual bool NotifyChildValue(GridEntry pe, int type) { return pe.NotifyValueGivenParent(pe.GetValueOwner(),type); } internal virtual bool NotifyValue(int type) { // KILLME, [....], 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)); } ////// /// Refresh the current GridEntry's value and it's children /// 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(); } ////// /// Sets the value of this GridEntry from text /// 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; } ////// /// 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 { GridEntry owner = null; private delegate void SelectDelegate(AccessibleSelection flags); 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; } } [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 { 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); } } ////// /// Returns the currently focused child, if any. /// Returns this if the object itself is focused. /// public override AccessibleObject GetFocused() { if (owner.Focus) { return this; } else { return null; } } ////// /// Navigate to the next or previous grid entry. /// [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); } } } public class DisplayNameSortComparer : IComparer { public int Compare(object left, object right) { // review: ([....]) 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; } } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007.
Link Menu

This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- basecomparevalidator.cs
- Canvas.cs
- VectorCollectionValueSerializer.cs
- UrlMappingsSection.cs
- EventProviderTraceListener.cs
- ConfigurationManagerInternalFactory.cs
- MimePart.cs
- XmlNode.cs
- HwndProxyElementProvider.cs
- StreamDocument.cs
- FixedSOMLineRanges.cs
- ToolStripHighContrastRenderer.cs
- LayoutUtils.cs
- Accessible.cs
- CreateParams.cs
- ScriptingProfileServiceSection.cs
- Command.cs
- PrivilegedConfigurationManager.cs
- CqlErrorHelper.cs
- XmlDataSourceNodeDescriptor.cs
- Size.cs
- LineServicesRun.cs
- ImplicitInputBrush.cs
- OracleBFile.cs
- QueryCacheManager.cs
- ForeignKeyConstraint.cs
- WebException.cs
- TemplateControlBuildProvider.cs
- StateMachineSubscriptionManager.cs
- _SSPISessionCache.cs
- LogRestartAreaEnumerator.cs
- ToolZone.cs
- LicenseManager.cs
- XmlToDatasetMap.cs
- ObjectDataSourceMethodEventArgs.cs
- _NtlmClient.cs
- ComponentEditorForm.cs
- OAVariantLib.cs
- Environment.cs
- PropertyGridEditorPart.cs
- UnicastIPAddressInformationCollection.cs
- KernelTypeValidation.cs
- MultipartContentParser.cs
- CustomTypeDescriptor.cs
- ScrollBar.cs
- WebPartEditorApplyVerb.cs
- SystemGatewayIPAddressInformation.cs
- LineServices.cs
- OperatorExpressions.cs
- TcpTransportSecurity.cs
- RawUIStateInputReport.cs
- ITreeGenerator.cs
- TableLayoutSettingsTypeConverter.cs
- ChtmlMobileTextWriter.cs
- QueryCacheKey.cs
- FillBehavior.cs
- SoundPlayerAction.cs
- HandleExceptionArgs.cs
- BamlResourceSerializer.cs
- BinHexDecoder.cs
- FindSimilarActivitiesVerb.cs
- HWStack.cs
- MemberAccessException.cs
- PolicyException.cs
- StyleModeStack.cs
- WorkflowApplicationException.cs
- QueryResponse.cs
- DeliveryStrategy.cs
- HyperLink.cs
- Ops.cs
- DoubleLinkList.cs
- Delay.cs
- DashStyles.cs
- SizeFConverter.cs
- Decorator.cs
- ColorDialog.cs
- WCFBuildProvider.cs
- IgnorePropertiesAttribute.cs
- MultitargetUtil.cs
- BufferedGraphics.cs
- FilteredAttributeCollection.cs
- SmtpReplyReaderFactory.cs
- AccessorTable.cs
- MessageLoggingElement.cs
- AnnouncementDispatcherAsyncResult.cs
- XmlComment.cs
- ProfilePropertySettingsCollection.cs
- FormViewDesigner.cs
- FormViewUpdateEventArgs.cs
- ObjectItemAttributeAssemblyLoader.cs
- AuthenticationModulesSection.cs
- SqlRetyper.cs
- PersianCalendar.cs
- WindowsScrollBar.cs
- StoreItemCollection.Loader.cs
- EventlogProvider.cs
- Bezier.cs
- QueryOperator.cs
- ConfigurationElementProperty.cs
- DependentList.cs