//------------------------------------------------------------------------------ // // Copyright (c) Microsoft Corporation. All rights reserved. // //------------------------------------------------------------------------------ /* */ namespace System.ComponentModel { using Microsoft.Win32; using System; using System.Collections; using System.Collections.Specialized; using System.ComponentModel.Design; using System.Diagnostics; using System.Globalization; using System.Reflection; using System.Runtime.InteropServices; using System.Runtime.Serialization.Formatters; using System.Security; using System.Security.Permissions; /// /// /// /// ReflectPropertyDescriptor defines a property. Properties are the main way that a user can /// set up the state of a component. /// The ReflectPropertyDescriptor class takes a component class that the property lives on, /// a property name, the type of the property, and various attributes for the /// property. /// For a property named XXX of type YYY, the associated component class is /// required to implement two methods of the following /// form: /// /// /// public YYY GetXXX(); /// public void SetXXX(YYY value); /// /// The component class can optionally implement two additional methods of /// the following form: /// /// public boolean ShouldSerializeXXX(); /// public void ResetXXX(); /// /// These methods deal with a property's default value. The ShouldSerializeXXX() /// method returns true if the current value of the XXX property is different /// than it's default value, so that it should be persisted out. The ResetXXX() /// method resets the XXX property to its default value. If the ReflectPropertyDescriptor /// includes the default value of the property (using the DefaultValueAttribute), /// the ShouldSerializeXXX() and ResetXXX() methods are ignored. /// If the ReflectPropertyDescriptor includes a reference to an editor /// then that value editor will be used to /// edit the property. Otherwise, a system-provided editor will be used. /// Various attributes can be passed to the ReflectPropertyDescriptor, as are described in /// Attribute. /// ReflectPropertyDescriptors can be obtained by a user programmatically through the /// ComponentManager. /// [HostProtection(SharedState = true)] internal sealed class ReflectPropertyDescriptor : PropertyDescriptor { private static readonly Type[] argsNone = new Type[0]; private static readonly object noValue = new object(); private static TraceSwitch PropDescCreateSwitch = new TraceSwitch("PropDescCreate", "ReflectPropertyDescriptor: Dump errors when creating property info"); private static TraceSwitch PropDescUsageSwitch = new TraceSwitch("PropDescUsage", "ReflectPropertyDescriptor: Debug propertydescriptor usage"); private static TraceSwitch PropDescSwitch = new TraceSwitch("PropDesc", "ReflectPropertyDescriptor: Debug property descriptor"); private static readonly int BitDefaultValueQueried = BitVector32.CreateMask(); private static readonly int BitGetQueried = BitVector32.CreateMask(BitDefaultValueQueried); private static readonly int BitSetQueried = BitVector32.CreateMask(BitGetQueried); private static readonly int BitShouldSerializeQueried = BitVector32.CreateMask(BitSetQueried); private static readonly int BitResetQueried = BitVector32.CreateMask(BitShouldSerializeQueried); private static readonly int BitChangedQueried = BitVector32.CreateMask(BitResetQueried); private static readonly int BitIPropChangedQueried = BitVector32.CreateMask(BitChangedQueried); private static readonly int BitReadOnlyChecked = BitVector32.CreateMask(BitIPropChangedQueried); private static readonly int BitAmbientValueQueried = BitVector32.CreateMask(BitReadOnlyChecked); private static readonly int BitSetOnDemand = BitVector32.CreateMask(BitAmbientValueQueried); BitVector32 state = new BitVector32(); // Contains the state bits for this proeprty descriptor. Type componentClass; // used to determine if we should all on us or on the designer Type type; // the data type of the property object defaultValue; // the default value of the property (or noValue) object ambientValue; // the ambient value of the property (or noValue) PropertyInfo propInfo; // the property info MethodInfo getMethod; // the property get method MethodInfo setMethod; // the property set method MethodInfo shouldSerializeMethod; // the should serialize method MethodInfo resetMethod; // the reset property method EventDescriptor realChangedEvent; // Changed event handler on object EventDescriptor realIPropChangedEvent; // INotifyPropertyChanged.PropertyChanged event handler on object Type receiverType; // Only set if we are an extender /// /// The main constructor for ReflectPropertyDescriptors. /// public ReflectPropertyDescriptor(Type componentClass, string name, Type type, Attribute[] attributes) : base(name, attributes) { Debug.WriteLineIf(PropDescCreateSwitch.TraceVerbose, "Creating ReflectPropertyDescriptor for " + componentClass.FullName + "." + name); try { if (type == null) { Debug.WriteLineIf(PropDescCreateSwitch.TraceVerbose, "type == null, name == " + name); throw new ArgumentException(SR.GetString(SR.ErrorInvalidPropertyType, name)); } if (componentClass == null) { Debug.WriteLineIf(PropDescCreateSwitch.TraceVerbose, "componentClass == null, name == " + name); throw new ArgumentException(SR.GetString(SR.InvalidNullArgument, "componentClass")); } this.type = type; this.componentClass = componentClass; } catch (Exception t) { Debug.Fail("Property '" + name + "' on component " + componentClass.FullName + " failed to init."); Debug.Fail(t.ToString()); throw t; } } /// /// A constructor for ReflectPropertyDescriptors that have no attributes. /// public ReflectPropertyDescriptor(Type componentClass, string name, Type type, PropertyInfo propInfo, MethodInfo getMethod, MethodInfo setMethod, Attribute[] attrs) : this(componentClass, name, type, attrs) { this.propInfo = propInfo; this.getMethod = getMethod; this.setMethod = setMethod; if (getMethod != null && propInfo != null && setMethod == null ) state[BitGetQueried | BitSetOnDemand] = true; else state[BitGetQueried | BitSetQueried] = true; } /// /// A constructor for ReflectPropertyDescriptors that creates an extender property. /// public ReflectPropertyDescriptor(Type componentClass, string name, Type type, Type receiverType, MethodInfo getMethod, MethodInfo setMethod, Attribute[] attrs) : this(componentClass, name, type, attrs) { this.receiverType = receiverType; this.getMethod = getMethod; this.setMethod = setMethod; state[BitGetQueried | BitSetQueried] = true; } /// /// This constructor takes an existing ReflectPropertyDescriptor and modifies it by merging in the /// passed-in attributes. /// public ReflectPropertyDescriptor(Type componentClass, PropertyDescriptor oldReflectPropertyDescriptor, Attribute[] attributes) : base(oldReflectPropertyDescriptor, attributes) { this.componentClass = componentClass; this.type = oldReflectPropertyDescriptor.PropertyType; if (componentClass == null) { throw new ArgumentException(SR.GetString(SR.InvalidNullArgument, "componentClass")); } // If the classes are the same, we can potentially optimize the method fetch because // the old property descriptor may already have it. // ReflectPropertyDescriptor oldProp = oldReflectPropertyDescriptor as ReflectPropertyDescriptor; if (oldProp != null) { if (oldProp.ComponentType == componentClass) { propInfo = oldProp.propInfo; getMethod = oldProp.getMethod; setMethod = oldProp.setMethod; shouldSerializeMethod = oldProp.shouldSerializeMethod; resetMethod = oldProp.resetMethod; defaultValue = oldProp.defaultValue; ambientValue = oldProp.ambientValue; state = oldProp.state; } // Now we must figure out what to do with our default value. First, check to see // if the caller has provided an new default value attribute. If so, use it. Otherwise, // just let it be and it will be picked up on demand. // if (attributes != null) { foreach(Attribute a in attributes) { DefaultValueAttribute dva = a as DefaultValueAttribute; if (dva != null) { defaultValue = dva.Value; // Default values for enums are often stored as their underlying integer type: if (defaultValue != null && PropertyType.IsEnum && PropertyType.GetEnumUnderlyingType() == defaultValue.GetType()) { defaultValue = Enum.ToObject(PropertyType, defaultValue); } state[BitDefaultValueQueried] = true; } else { AmbientValueAttribute ava = a as AmbientValueAttribute; if (ava != null) { ambientValue = ava.Value; state[BitAmbientValueQueried] = true; } } } } } #if DEBUG else if (oldReflectPropertyDescriptor is DebugReflectPropertyDescriptor) { DebugReflectPropertyDescriptor oldProp1 = (DebugReflectPropertyDescriptor)oldReflectPropertyDescriptor; if (oldProp1.ComponentType == componentClass) { propInfo = oldProp1.propInfo; getMethod = oldProp1.getMethod; setMethod = oldProp1.setMethod; shouldSerializeMethod = oldProp1.shouldSerializeMethod; resetMethod = oldProp1.resetMethod; defaultValue = oldProp1.defaultValue; ambientValue = oldProp1.ambientValue; state = oldProp1.state; } // Now we must figure out what to do with our default value. First, check to see // if the caller has provided an new default value attribute. If so, use it. Otherwise, // just let it be and it will be picked up on demand. // if (attributes != null) { foreach(Attribute a in attributes) { if (a is DefaultValueAttribute) { defaultValue = ((DefaultValueAttribute)a).Value; state[BitDefaultValueQueried] = true; } else if (a is AmbientValueAttribute) { ambientValue = ((AmbientValueAttribute)a).Value; state[BitAmbientValueQueried] = true; } } } } #endif } /// /// Retrieves the ambient value for this property. /// private object AmbientValue { get { if (!state[BitAmbientValueQueried]) { state[BitAmbientValueQueried] = true; Attribute a = Attributes[typeof(AmbientValueAttribute)]; if (a != null) { ambientValue = ((AmbientValueAttribute)a).Value; } else { ambientValue = noValue; } } return ambientValue; } } /// /// The EventDescriptor for the "{propertyname}Changed" event on the component, or null if there isn't one for this property. /// private EventDescriptor ChangedEventValue { get { if (!state[BitChangedQueried]) { state[BitChangedQueried] = true; realChangedEvent = TypeDescriptor.GetEvents(ComponentType)[string.Format(CultureInfo.InvariantCulture, "{0}Changed", Name)]; } return realChangedEvent; } /* The following code has been removed to fix FXCOP violations. The code is left here incase it needs to be resurrected in the future. set { realChangedEvent = value; state[BitChangedQueried] = true; } */ } /// /// The EventDescriptor for the INotifyPropertyChanged.PropertyChanged event on the component, or null if there isn't one for this property. /// private EventDescriptor IPropChangedEventValue { get { if (!state[BitIPropChangedQueried]) { state[BitIPropChangedQueried] = true; if (typeof(INotifyPropertyChanged).IsAssignableFrom(ComponentType)) { realIPropChangedEvent = TypeDescriptor.GetEvents(typeof(INotifyPropertyChanged))["PropertyChanged"]; } } return realIPropChangedEvent; } set { realIPropChangedEvent = value; state[BitIPropChangedQueried] = true; } } /// /// Retrieves the type of the component this PropertyDescriptor is bound to. /// public override Type ComponentType { get { return componentClass; } } /// /// Retrieves the default value for this property. /// private object DefaultValue { get { if (!state[BitDefaultValueQueried]) { state[BitDefaultValueQueried] = true; Attribute a = Attributes[typeof(DefaultValueAttribute)]; if (a != null) { defaultValue = ((DefaultValueAttribute)a).Value; // Default values for enums are often stored as their underlying integer type: if (defaultValue != null && PropertyType.IsEnum && PropertyType.GetEnumUnderlyingType() == defaultValue.GetType()) { defaultValue = Enum.ToObject(PropertyType, defaultValue); } } else { defaultValue = noValue; } } return defaultValue; } } /// /// The GetMethod for this property /// private MethodInfo GetMethodValue { get { if (!state[BitGetQueried]) { state[BitGetQueried] = true; if (receiverType == null) { if (propInfo == null) { BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.GetProperty; propInfo = componentClass.GetProperty(Name, bindingFlags, null, PropertyType, new Type[0], new ParameterModifier[0]); } if (propInfo != null) { getMethod = propInfo.GetGetMethod(true); } if (getMethod == null) { throw new InvalidOperationException(SR.GetString(SR.ErrorMissingPropertyAccessors, componentClass.FullName + "." + Name)); } } else { getMethod = FindMethod(componentClass, "Get" + Name, new Type[] { receiverType }, type); if (getMethod == null) { throw new ArgumentException(SR.GetString(SR.ErrorMissingPropertyAccessors, Name)); } } } return getMethod; } /* The following code has been removed to fix FXCOP violations. The code is left here incase it needs to be resurrected in the future. set { state[BitGetQueried] = true; getMethod = value; } */ } /// /// Determines if this property is an extender property. /// private bool IsExtender { get { return (receiverType != null); } } /// /// Indicates whether this property is read only. /// public override bool IsReadOnly { get { return SetMethodValue == null || ((ReadOnlyAttribute)Attributes[typeof(ReadOnlyAttribute)]).IsReadOnly; } } /// /// Retrieves the type of the property. /// public override Type PropertyType { get { return type; } } /// /// Access to the reset method, if one exists for this property. /// private MethodInfo ResetMethodValue { get { if (!state[BitResetQueried]) { state[BitResetQueried] = true; Type[] args; if (receiverType == null) { args = argsNone; } else { args = new Type[] {receiverType}; } #if FEATURE_MONO_CAS IntSecurity.FullReflection.Assert(); try { #endif resetMethod = FindMethod(componentClass, "Reset" + Name, args, typeof(void), /* publicOnly= */ false); #if FEATURE_MONO_CAS } finally { CodeAccessPermission.RevertAssert(); } #endif } return resetMethod; } /* The following code has been removed to fix FXCOP violations. The code is left here incase it needs to be resurrected in the future. set { state[BitResetQueried] = true; resetMethod = value; } */ } /// /// Accessor for the set method /// private MethodInfo SetMethodValue { get { if (!state[BitSetQueried] && state[BitSetOnDemand]) { state[BitSetQueried] = true; BindingFlags bindingFlags = BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance; string name = propInfo.Name; if (setMethod == null) { for (Type t = ComponentType.BaseType; t != null && t != typeof(object); t = t.BaseType) { if (t == null) { break; } PropertyInfo p = t.GetProperty(name, bindingFlags, null, PropertyType, new Type[0], null); if (p != null) { setMethod = p.GetSetMethod(); if (setMethod != null) { break; } } } } } if (!state[BitSetQueried]) { state[BitSetQueried] = true; if (receiverType == null) { if (propInfo == null) { BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.GetProperty; propInfo = componentClass.GetProperty(Name, bindingFlags, null, PropertyType, new Type[0], new ParameterModifier[0]); } if (propInfo != null) { setMethod = propInfo.GetSetMethod(true); } } else { setMethod = FindMethod(componentClass, "Set" + Name, new Type[] { receiverType, type}, typeof(void)); } } return setMethod; } /* The following code has been removed to fix FXCOP violations. The code is left here incase it needs to be resurrected in the future. set { state[BitSetQueried] = true; setMethod = value; } */ } /// /// Accessor for the ShouldSerialize method. /// private MethodInfo ShouldSerializeMethodValue { get { if (!state[BitShouldSerializeQueried]) { state[BitShouldSerializeQueried] = true; Type[] args; if (receiverType == null) { args = argsNone; } else { args = new Type[] {receiverType}; } #if FEATURE_MONO_CAS IntSecurity.FullReflection.Assert(); try { #endif shouldSerializeMethod = FindMethod(componentClass, "ShouldSerialize" + Name, args, typeof(Boolean), /* publicOnly= */ false); #if FEATURE_MONO_CAS } finally { CodeAccessPermission.RevertAssert(); } #endif } return shouldSerializeMethod; } /* The following code has been removed to fix FXCOP violations. The code is left here incase it needs to be resurrected in the future. set { state[BitShouldSerializeQueried] = true; shouldSerializeMethod = value; } */ } /// /// Allows interested objects to be notified when this property changes. /// public override void AddValueChanged(object component, EventHandler handler) { if (component == null) throw new ArgumentNullException("component"); if (handler == null) throw new ArgumentNullException("handler"); // If there's an event called Changed, hook the caller's handler directly up to that on the component EventDescriptor changedEvent = ChangedEventValue; if (changedEvent != null && changedEvent.EventType.IsInstanceOfType(handler)) { changedEvent.AddEventHandler(component, handler); } // Otherwise let the base class add the handler to its ValueChanged event for this component else { // Special case: If this will be the FIRST handler added for this component, and the component implements // INotifyPropertyChanged, the property descriptor must START listening to the generic PropertyChanged event if (GetValueChangedHandler(component) == null) { EventDescriptor iPropChangedEvent = IPropChangedEventValue; if (iPropChangedEvent != null) { iPropChangedEvent.AddEventHandler(component, new PropertyChangedEventHandler(OnINotifyPropertyChanged)); } } base.AddValueChanged(component, handler); } } internal bool ExtenderCanResetValue(IExtenderProvider provider, object component) { if (DefaultValue != noValue) { return !object.Equals(ExtenderGetValue(provider, component),defaultValue); } MethodInfo reset = ResetMethodValue; if (reset != null) { MethodInfo shouldSerialize = ShouldSerializeMethodValue; if (shouldSerialize != null) { try { provider = (IExtenderProvider)GetInvocationTarget(componentClass, provider); return (bool)shouldSerialize.Invoke(provider, new object[] { component}); } catch {} } return true; } return false; } internal Type ExtenderGetReceiverType() { return receiverType; } internal Type ExtenderGetType(IExtenderProvider provider) { return PropertyType; } internal object ExtenderGetValue(IExtenderProvider provider, object component) { if (provider != null) { provider = (IExtenderProvider)GetInvocationTarget(componentClass, provider); return GetMethodValue.Invoke(provider, new object[] { component}); } return null; } internal void ExtenderResetValue(IExtenderProvider provider, object component, PropertyDescriptor notifyDesc) { if (DefaultValue != noValue) { ExtenderSetValue(provider, component, DefaultValue, notifyDesc); } else if (AmbientValue != noValue) { ExtenderSetValue(provider, component, AmbientValue, notifyDesc); } else if (ResetMethodValue != null) { ISite site = GetSite(component); IComponentChangeService changeService = null; object oldValue = null; object newValue; // Announce that we are about to change this component // if (site != null) { changeService = (IComponentChangeService)site.GetService(typeof(IComponentChangeService)); Debug.Assert(!CompModSwitches.CommonDesignerServices.Enabled || changeService != null, "IComponentChangeService not found"); } // Make sure that it is ok to send the onchange events // if (changeService != null) { oldValue = ExtenderGetValue(provider, component); try { changeService.OnComponentChanging(component, notifyDesc); } catch (CheckoutException coEx) { if (coEx == CheckoutException.Canceled) { return; } throw coEx; } } provider = (IExtenderProvider)GetInvocationTarget(componentClass, provider); if (ResetMethodValue != null) { ResetMethodValue.Invoke(provider, new object[] { component}); // Now notify the change service that the change was successful. // if (changeService != null) { newValue = ExtenderGetValue(provider, component); changeService.OnComponentChanged(component, notifyDesc, oldValue, newValue); } } } } internal void ExtenderSetValue(IExtenderProvider provider, object component, object value, PropertyDescriptor notifyDesc) { if (provider != null) { ISite site = GetSite(component); IComponentChangeService changeService = null; object oldValue = null; // Announce that we are about to change this component // if (site != null) { changeService = (IComponentChangeService)site.GetService(typeof(IComponentChangeService)); Debug.Assert(!CompModSwitches.CommonDesignerServices.Enabled || changeService != null, "IComponentChangeService not found"); } // Make sure that it is ok to send the onchange events // if (changeService != null) { oldValue = ExtenderGetValue(provider, component); try { changeService.OnComponentChanging(component, notifyDesc); } catch (CheckoutException coEx) { if (coEx == CheckoutException.Canceled) { return; } throw coEx; } } provider = (IExtenderProvider)GetInvocationTarget(componentClass, provider); if (SetMethodValue != null) { SetMethodValue.Invoke(provider, new object[] { component, value}); // Now notify the change service that the change was successful. // if (changeService != null) { changeService.OnComponentChanged(component, notifyDesc, oldValue, value); } } } } internal bool ExtenderShouldSerializeValue(IExtenderProvider provider, object component) { provider = (IExtenderProvider)GetInvocationTarget(componentClass, provider); if (IsReadOnly) { if (ShouldSerializeMethodValue != null) { try { return (bool)ShouldSerializeMethodValue.Invoke(provider, new object[] {component}); } catch {} } return Attributes.Contains(DesignerSerializationVisibilityAttribute.Content); } else if (DefaultValue == noValue) { if (ShouldSerializeMethodValue != null) { try { return (bool)ShouldSerializeMethodValue.Invoke(provider, new object[] {component}); } catch {} } return true; } return !object.Equals(DefaultValue, ExtenderGetValue(provider, component)); } /// /// Indicates whether reset will change the value of the component. If there /// is a DefaultValueAttribute, then this will return true if getValue returns /// something different than the default value. If there is a reset method and /// a ShouldSerialize method, this will return what ShouldSerialize returns. /// If there is just a reset method, this always returns true. If none of these /// cases apply, this returns false. /// public override bool CanResetValue(object component) { if (IsExtender || IsReadOnly) { return false; } if (DefaultValue != noValue) { return !object.Equals(GetValue(component),DefaultValue); } if (ResetMethodValue != null) { if (ShouldSerializeMethodValue != null) { component = GetInvocationTarget(componentClass, component); try { return (bool)ShouldSerializeMethodValue.Invoke(component, null); } catch {} } return true; } if (AmbientValue != noValue) { return ShouldSerializeValue(component); } return false; } protected override void FillAttributes(IList attributes) { Debug.Assert(componentClass != null, "Must have a component class for FillAttributes"); // // The order that we fill in attributes is critical. The list of attributes will be // filtered so that matching attributes at the end of the list replace earlier matches // (last one in wins). Therefore, the four categories of attributes we add must be // added as follows: // // 1. Attributes of the property type. These are the lowest level and should be // overwritten by any newer attributes. // // 2. Attributes obtained from any SpecificTypeAttribute. These supercede attributes // for the property type. // // 3. Attributes of the property itself, from base class to most derived. This way // derived class attributes replace base class attributes. // // 4. Attributes from our base MemberDescriptor. While this seems opposite of what // we want, MemberDescriptor only has attributes if someone passed in a new // set in the constructor. Therefore, these attributes always // supercede existing values. // // We need to include attributes from the type of the property. // foreach (Attribute typeAttr in TypeDescriptor.GetAttributes(PropertyType)) { attributes.Add(typeAttr); } // NOTE : Must look at method OR property, to handle the case of Extender properties... // // Note : Because we are using BindingFlags.DeclaredOnly it is more effcient to re-aquire // : the property info, rather than use the one we have cached. The one we have cached // : may ave come from a base class, meaning we will request custom metadata for this // : class twice. BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.DeclaredOnly; Type currentReflectType = componentClass; int depth = 0; // First, calculate the depth of the object hierarchy. We do this so we can do a single // object create for an array of attributes. // while(currentReflectType != null && currentReflectType != typeof(object)) { depth++; currentReflectType = currentReflectType.BaseType; } // Now build up an array in reverse order // if (depth > 0) { currentReflectType = componentClass; Attribute[][] attributeStack = new Attribute[depth][]; while(currentReflectType != null && currentReflectType != typeof(object)) { MemberInfo memberInfo = null; // Fill in our member info so we can get at the custom attributes. // if (IsExtender) { //receiverType is used to avoid ambitiousness when there are overloads for the get method. memberInfo = currentReflectType.GetMethod("Get" + Name, bindingFlags, null, new Type[] { receiverType }, null); } else { memberInfo = currentReflectType.GetProperty(Name, bindingFlags, null, PropertyType, new Type[0], new ParameterModifier[0]); } // Get custom attributes for the member info. // if (memberInfo != null) { attributeStack[--depth] = ReflectTypeDescriptionProvider.ReflectGetAttributes(memberInfo); } // Ready for the next loop iteration. // currentReflectType = currentReflectType.BaseType; } // Look in the attribute stack for AttributeProviders // foreach(Attribute[] attributeArray in attributeStack) { if (attributeArray != null) { foreach(Attribute attr in attributeArray) { AttributeProviderAttribute sta = attr as AttributeProviderAttribute; if (sta != null) { Type specificType = Type.GetType(sta.TypeName); if (specificType != null) { Attribute[] stAttrs = null; if (!String.IsNullOrEmpty(sta.PropertyName)) { MemberInfo[] milist = specificType.GetMember(sta.PropertyName); if (milist.Length > 0 && milist[0] != null) { stAttrs = ReflectTypeDescriptionProvider.ReflectGetAttributes(milist[0]); } } else { stAttrs = ReflectTypeDescriptionProvider.ReflectGetAttributes(specificType); } if (stAttrs != null) { foreach(Attribute stAttr in stAttrs) { attributes.Add(stAttr); } } } } } } } // Now trawl the attribute stack so that we add attributes // from base class to most derived. // foreach(Attribute[] attributeArray in attributeStack) { if (attributeArray != null) { foreach(Attribute attr in attributeArray) { attributes.Add(attr); } } } } // Include the base attributes. These override all attributes on the actual // property, so we want to add them last. // base.FillAttributes(attributes); // Finally, override any form of ReadOnlyAttribute. // if (SetMethodValue == null) { attributes.Add(ReadOnlyAttribute.Yes); } } /// /// Retrieves the current value of the property on component, /// invoking the getXXX method. An exception in the getXXX /// method will pass through. /// public override object GetValue(object component) { #if DEBUG if (PropDescUsageSwitch.TraceVerbose) { string compName = "(null)"; if (component != null) compName = component.ToString(); Debug.WriteLine("[" + Name + "]: GetValue(" + compName + ")"); } #endif if (IsExtender) { Debug.WriteLineIf(PropDescUsageSwitch.TraceVerbose, "[" + Name + "]: ---> returning: null"); return null; } Debug.Assert(component != null, "GetValue must be given a component"); if (component != null) { component = GetInvocationTarget(componentClass, component); try { return SecurityUtils.MethodInfoInvoke(GetMethodValue, component, null); } catch (Exception t) { string name = null; IComponent comp = component as IComponent; if (comp != null) { ISite site = comp.Site; if (site != null && site.Name != null) { name = site.Name; } } if (name == null) { name = component.GetType().FullName; } if (t is TargetInvocationException) { t = t.InnerException; } string message = t.Message; if (message == null) { message = t.GetType().Name; } throw new TargetInvocationException(SR.GetString(SR.ErrorPropertyAccessorException, Name, name, message), t); } } Debug.WriteLineIf(PropDescUsageSwitch.TraceVerbose, "[" + Name + "]: ---> returning: null"); return null; } /// /// Handles INotifyPropertyChanged.PropertyChange events from components. /// If event pertains to this property, issue a ValueChanged event. /// /// internal void OnINotifyPropertyChanged(object component, PropertyChangedEventArgs e) { if (String.IsNullOrEmpty(e.PropertyName) || String.Compare(e.PropertyName, Name, true, System.Globalization.CultureInfo.InvariantCulture) == 0) { OnValueChanged(component, e); } } /// /// This should be called by your property descriptor implementation /// when the property value has changed. /// protected override void OnValueChanged(object component, EventArgs e) { if (state[BitChangedQueried] && realChangedEvent == null) { base.OnValueChanged(component, e); } } /// /// Allows interested objects to be notified when this property changes. /// public override void RemoveValueChanged(object component, EventHandler handler) { if (component == null) throw new ArgumentNullException("component"); if (handler == null) throw new ArgumentNullException("handler"); // If there's an event called Changed, we hooked the caller's // handler directly up to that on the component, so remove it now. EventDescriptor changedEvent = ChangedEventValue; if (changedEvent != null && changedEvent.EventType.IsInstanceOfType(handler)) { changedEvent.RemoveEventHandler(component, handler); } // Otherwise the base class added the handler to its ValueChanged // event for this component, so let the base class remove it now. else { base.RemoveValueChanged(component, handler); // Special case: If that was the LAST handler removed for this component, and the component implements // INotifyPropertyChanged, the property descriptor must STOP listening to the generic PropertyChanged event if (GetValueChangedHandler(component) == null) { EventDescriptor iPropChangedEvent = IPropChangedEventValue; if (iPropChangedEvent != null) { iPropChangedEvent.RemoveEventHandler(component, new PropertyChangedEventHandler(OnINotifyPropertyChanged)); } } } } /// /// Will reset the default value for this property on the component. If /// there was a default value passed in as a DefaultValueAttribute, that /// value will be set as the value of the property on the component. If /// there was no default value passed in, a ResetXXX method will be looked /// for. If one is found, it will be invoked. If one is not found, this /// is a nop. /// public override void ResetValue(object component) { object invokee = GetInvocationTarget(componentClass, component); if (DefaultValue != noValue) { SetValue(component, DefaultValue); } else if (AmbientValue != noValue) { SetValue(component, AmbientValue); } else if (ResetMethodValue != null) { ISite site = GetSite(component); IComponentChangeService changeService = null; object oldValue = null; object newValue; // Announce that we are about to change this component // if (site != null) { changeService = (IComponentChangeService)site.GetService(typeof(IComponentChangeService)); Debug.Assert(!CompModSwitches.CommonDesignerServices.Enabled || changeService != null, "IComponentChangeService not found"); } // Make sure that it is ok to send the onchange events // if (changeService != null) { // invokee might be a type from mscorlib or system, GetMethodValue might return a NonPublic method oldValue = SecurityUtils.MethodInfoInvoke(GetMethodValue, invokee, (object[])null); try { changeService.OnComponentChanging(component, this); } catch (CheckoutException coEx) { if (coEx == CheckoutException.Canceled) { return; } throw coEx; } } if (ResetMethodValue != null) { SecurityUtils.MethodInfoInvoke(ResetMethodValue, invokee, (object[])null); // Now notify the change service that the change was successful. // if (changeService != null) { newValue = SecurityUtils.MethodInfoInvoke(GetMethodValue, invokee, (object[])null); changeService.OnComponentChanged(component, this, oldValue, newValue); } } } } /// /// This will set value to be the new value of this property on the /// component by invoking the setXXX method on the component. If the /// value specified is invalid, the component should throw an exception /// which will be passed up. The component designer should design the /// property so that getXXX following a setXXX should return the value /// passed in if no exception was thrown in the setXXX call. /// public override void SetValue(object component, object value) { #if DEBUG if (PropDescUsageSwitch.TraceVerbose) { string compName = "(null)"; string valName = "(null)"; if (component != null) compName = component.ToString(); if (value != null) valName = value.ToString(); Debug.WriteLine("[" + Name + "]: SetValue(" + compName + ", " + valName + ")"); } #endif if (component != null) { ISite site = GetSite(component); IComponentChangeService changeService = null; object oldValue = null; object invokee = GetInvocationTarget(componentClass, component); if (!IsReadOnly) { // Announce that we are about to change this component // if (site != null) { changeService = (IComponentChangeService)site.GetService(typeof(IComponentChangeService)); Debug.Assert(!CompModSwitches.CommonDesignerServices.Enabled || changeService != null, "IComponentChangeService not found"); } // Make sure that it is ok to send the onchange events // if (changeService != null) { oldValue = SecurityUtils.MethodInfoInvoke(GetMethodValue, invokee, (object[])null); try { changeService.OnComponentChanging(component, this); } catch (CheckoutException coEx) { if (coEx == CheckoutException.Canceled) { return; } throw coEx; } } try { try { SecurityUtils.MethodInfoInvoke(SetMethodValue, invokee, new object[] { value }); OnValueChanged(invokee, EventArgs.Empty); } catch (Exception t) { // Give ourselves a chance to unwind properly before rethrowing the exception. // value = oldValue; // If there was a problem setting the controls property then we get: // ArgumentException (from properties set method) // ==> Becomes inner exception of TargetInvocationException // ==> caught here if (t is TargetInvocationException && t.InnerException != null) { // Propagate the original exception up throw t.InnerException; } else { throw t; } } } finally { // Now notify the change service that the change was successful. // if (changeService != null) { changeService.OnComponentChanged(component, this, oldValue, value); } } } } } /// /// Indicates whether the value of this property needs to be persisted. In /// other words, it indicates whether the state of the property is distinct /// from when the component is first instantiated. If there is a default /// value specified in this ReflectPropertyDescriptor, it will be compared against the /// property's current value to determine this. If there is't, the /// ShouldSerializeXXX method is looked for and invoked if found. If both /// these routes fail, true will be returned. /// /// If this returns false, a tool should not persist this property's value. /// public override bool ShouldSerializeValue(object component) { component = GetInvocationTarget(componentClass, component); if (IsReadOnly) { if (ShouldSerializeMethodValue != null) { try { return (bool)ShouldSerializeMethodValue.Invoke(component, null); } catch {} } return Attributes.Contains(DesignerSerializationVisibilityAttribute.Content); } else if (DefaultValue == noValue) { if (ShouldSerializeMethodValue != null) { try { return (bool)ShouldSerializeMethodValue.Invoke(component, null); } catch {} } return true; } return !object.Equals(DefaultValue, GetValue(component)); } /// /// Indicates whether value change notifications for this property may originate from outside the property /// descriptor, such as from the component itself (value=true), or whether notifications will only originate /// from direct calls made to PropertyDescriptor.SetValue (value=false). For example, the component may /// implement the INotifyPropertyChanged interface, or may have an explicit '{name}Changed' event for this property. /// public override bool SupportsChangeEvents { get { return IPropChangedEventValue != null || ChangedEventValue != null; } } /* The following code has been removed to fix FXCOP violations. The code is left here incase it needs to be resurrected in the future. /// /// A constructor for ReflectPropertyDescriptors that have no attributes. /// public ReflectPropertyDescriptor(Type componentClass, string name, Type type) : this(componentClass, name, type, (Attribute[])null) { } */ } }