e79aa3c0ed
Former-commit-id: a2155e9bd80020e49e72e86c44da02a8ac0e57a4
1203 lines
51 KiB
C#
1203 lines
51 KiB
C#
//------------------------------------------------------------------------------
|
|
// <copyright file="DebugReflectPropertyDescriptor.cs" company="Microsoft">
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
// </copyright>
|
|
//------------------------------------------------------------------------------
|
|
|
|
#if DEBUG
|
|
|
|
/*
|
|
|
|
This class exists in debug only. It is a complete copy of the
|
|
V1.0 TypeDescriptor object and is used to validate that the
|
|
behavior of the V2.0 TypeDescriptor matches 1.0 behavior.
|
|
|
|
*/
|
|
namespace System.ComponentModel {
|
|
using System.Runtime.Serialization.Formatters;
|
|
using System.Runtime.InteropServices;
|
|
using System.Diagnostics;
|
|
using System;
|
|
using System.Collections;
|
|
using System.Collections.Specialized;
|
|
using System.Reflection;
|
|
using Microsoft.Win32;
|
|
using System.Security;
|
|
using System.Security.Permissions;
|
|
using System.ComponentModel.Design;
|
|
using System.ComponentModel;
|
|
|
|
/// <internalonly/>
|
|
/// <devdoc>
|
|
/// <para>
|
|
/// DebugReflectPropertyDescriptor defines a property. Properties are the main way that a user can
|
|
/// set up the state of a component.
|
|
/// The DebugReflectPropertyDescriptor 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:
|
|
/// </para>
|
|
/// <code>
|
|
/// public YYY GetXXX();
|
|
/// public void SetXXX(YYY value);
|
|
/// </code>
|
|
/// The component class can optionally implement two additional methods of
|
|
/// the following form:
|
|
/// <code>
|
|
/// public boolean ShouldSerializeXXX();
|
|
/// public void ResetXXX();
|
|
/// </code>
|
|
/// 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 DebugReflectPropertyDescriptor
|
|
/// includes the default value of the property (using the DefaultValueAttribute),
|
|
/// the ShouldSerializeXXX() and ResetXXX() methods are ignored.
|
|
/// If the DebugReflectPropertyDescriptor 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 DebugReflectPropertyDescriptor, as are described in
|
|
/// Attribute.
|
|
/// ReflectPropertyDescriptors can be obtained by a user programmatically through the
|
|
/// ComponentManager.
|
|
/// </devdoc>
|
|
[HostProtection(SharedState = true)]
|
|
internal sealed class DebugReflectPropertyDescriptor : PropertyDescriptor {
|
|
|
|
private static readonly Type[] argsNone = new Type[0];
|
|
private static readonly object noValue = new object();
|
|
|
|
private static TraceSwitch PropDescCreateSwitch = new TraceSwitch("PropDescCreate", "DebugReflectPropertyDescriptor: Dump errors when creating property info");
|
|
private static TraceSwitch PropDescUsageSwitch = new TraceSwitch("PropDescUsage", "DebugReflectPropertyDescriptor: Debug propertydescriptor usage");
|
|
private static TraceSwitch PropDescSwitch = new TraceSwitch("PropDesc", "DebugReflectPropertyDescriptor: 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 BitReadOnlyChecked = BitVector32.CreateMask(BitChangedQueried);
|
|
private static readonly int BitAmbientValueQueried = BitVector32.CreateMask(BitReadOnlyChecked);
|
|
|
|
internal 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
|
|
|
|
internal object defaultValue; // the default value of the property (or noValue)
|
|
internal object ambientValue; // the ambient value of the property (or noValue)
|
|
|
|
internal PropertyInfo propInfo; // the property info
|
|
internal MethodInfo getMethod; // the property get method
|
|
internal MethodInfo setMethod; // the property set method
|
|
|
|
internal MethodInfo shouldSerializeMethod; // the should serialize method
|
|
internal MethodInfo resetMethod; // the reset property method
|
|
|
|
EventInfo realChangedEventInfo; // Changed event handler on object
|
|
|
|
Type receiverType; // Only set if we are an extender
|
|
private TypeConverter converter;
|
|
private object[] editors;
|
|
private Type[] editorTypes;
|
|
private int editorCount;
|
|
|
|
/// <devdoc>
|
|
/// The main constructor for ReflectPropertyDescriptors.
|
|
/// </devdoc>
|
|
public DebugReflectPropertyDescriptor(Type componentClass, string name, Type type,
|
|
Attribute[] attributes)
|
|
: base(name, attributes) {
|
|
|
|
Debug.WriteLineIf(PropDescCreateSwitch.TraceVerbose, "Creating DebugReflectPropertyDescriptor 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;
|
|
}
|
|
}
|
|
|
|
/// <devdoc>
|
|
/// A constructor for ReflectPropertyDescriptors that have no attributes.
|
|
/// </devdoc>
|
|
public DebugReflectPropertyDescriptor(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;
|
|
state[BitGetQueried | BitSetQueried] = true;
|
|
}
|
|
|
|
/// <devdoc>
|
|
/// A constructor for ReflectPropertyDescriptors that creates an extender property.
|
|
/// </devdoc>
|
|
public DebugReflectPropertyDescriptor(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;
|
|
}
|
|
|
|
/// <devdoc>
|
|
/// This constructor takes an existing DebugReflectPropertyDescriptor and modifies it by merging in the
|
|
/// passed-in attributes.
|
|
/// </devdoc>
|
|
public DebugReflectPropertyDescriptor(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.
|
|
//
|
|
if (oldReflectPropertyDescriptor is DebugReflectPropertyDescriptor) {
|
|
DebugReflectPropertyDescriptor oldProp = (DebugReflectPropertyDescriptor)oldReflectPropertyDescriptor;
|
|
|
|
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) {
|
|
if (a is DefaultValueAttribute) {
|
|
defaultValue = ((DefaultValueAttribute)a).Value;
|
|
state[BitDefaultValueQueried] = true;
|
|
}
|
|
else if (a is AmbientValueAttribute) {
|
|
ambientValue = ((AmbientValueAttribute)a).Value;
|
|
state[BitAmbientValueQueried] = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <devdoc>
|
|
/// Retrieves the ambient value for this property.
|
|
/// </devdoc>
|
|
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;
|
|
}
|
|
}
|
|
|
|
/// <devdoc>
|
|
/// The EventInfo for the changed event on the component, or null if there isn't one for this property.
|
|
/// </devdoc>
|
|
private EventInfo ChangedEventValue {
|
|
get {
|
|
if (!state[BitChangedQueried]) {
|
|
state[BitChangedQueried] = true;
|
|
realChangedEventInfo = ComponentType.GetEvent(Name + "Changed", BindingFlags.Public | BindingFlags.Instance);
|
|
}
|
|
return realChangedEventInfo;
|
|
}
|
|
|
|
/*
|
|
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 {
|
|
realChangedEventInfo = value;
|
|
state[BitChangedQueried] = true;
|
|
}
|
|
*/
|
|
}
|
|
|
|
/// <devdoc>
|
|
/// Retrieves the type of the component this PropertyDescriptor is bound to.
|
|
/// </devdoc>
|
|
public override Type ComponentType {
|
|
get {
|
|
return componentClass;
|
|
}
|
|
}
|
|
|
|
/// <devdoc>
|
|
/// <para>
|
|
/// Gets the type converter for this property.
|
|
/// </para>
|
|
/// </devdoc>
|
|
public override TypeConverter Converter {
|
|
get {
|
|
if (converter == null) {
|
|
TypeConverterAttribute attr = (TypeConverterAttribute)Attributes[typeof(TypeConverterAttribute)];
|
|
if (attr.ConverterTypeName != null && attr.ConverterTypeName.Length > 0) {
|
|
Type converterType = GetTypeFromName(attr.ConverterTypeName);
|
|
if (converterType != null && typeof(TypeConverter).IsAssignableFrom(converterType)) {
|
|
converter = (TypeConverter)CreateInstance(converterType);
|
|
}
|
|
}
|
|
|
|
if (converter == null) {
|
|
converter = DebugTypeDescriptor.GetConverter(PropertyType);
|
|
}
|
|
}
|
|
return converter;
|
|
}
|
|
}
|
|
|
|
/// <devdoc>
|
|
/// Retrieves the default value for this property.
|
|
/// </devdoc>
|
|
private object DefaultValue {
|
|
get {
|
|
if (!state[BitDefaultValueQueried]) {
|
|
state[BitDefaultValueQueried] = true;
|
|
Attribute a = Attributes[typeof(DefaultValueAttribute)];
|
|
if (a != null) {
|
|
defaultValue = ((DefaultValueAttribute)a).Value;
|
|
}
|
|
else {
|
|
defaultValue = noValue;
|
|
}
|
|
}
|
|
return defaultValue;
|
|
}
|
|
}
|
|
|
|
/// <devdoc>
|
|
/// The GetMethod for this property
|
|
/// </devdoc>
|
|
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;
|
|
}
|
|
*/
|
|
}
|
|
|
|
/// <devdoc>
|
|
/// Determines if this property is an extender property.
|
|
/// </devdoc>
|
|
private bool IsExtender {
|
|
get {
|
|
return (receiverType != null);
|
|
}
|
|
}
|
|
|
|
/// <devdoc>
|
|
/// Indicates whether this property is read only.
|
|
/// </devdoc>
|
|
public override bool IsReadOnly {
|
|
get {
|
|
return SetMethodValue == null || ((ReadOnlyAttribute)Attributes[typeof(ReadOnlyAttribute)]).IsReadOnly;
|
|
}
|
|
}
|
|
|
|
/// <devdoc>
|
|
/// Retrieves the type of the property.
|
|
/// </devdoc>
|
|
public override Type PropertyType {
|
|
get {
|
|
return type;
|
|
}
|
|
}
|
|
|
|
/// <devdoc>
|
|
/// Access to the reset method, if one exists for this property.
|
|
/// </devdoc>
|
|
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();
|
|
#endif
|
|
try {
|
|
resetMethod = FindMethod(componentClass, "Reset" + Name, args, typeof(void), /* publicOnly= */ false);
|
|
}
|
|
finally {
|
|
CodeAccessPermission.RevertAssert();
|
|
}
|
|
}
|
|
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;
|
|
}
|
|
*/
|
|
}
|
|
|
|
/// <devdoc>
|
|
/// Accessor for the set method
|
|
/// </devdoc>
|
|
private MethodInfo SetMethodValue {
|
|
get {
|
|
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;
|
|
}
|
|
*/
|
|
}
|
|
|
|
/// <devdoc>
|
|
/// Accessor for the ShouldSerialize method.
|
|
/// </devdoc>
|
|
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();
|
|
#endif
|
|
try {
|
|
shouldSerializeMethod = FindMethod(componentClass, "ShouldSerialize" + Name,
|
|
args, typeof(Boolean), /* publicOnly= */ false);
|
|
}
|
|
finally {
|
|
CodeAccessPermission.RevertAssert();
|
|
}
|
|
}
|
|
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;
|
|
}
|
|
*/
|
|
}
|
|
|
|
/// <devdoc>
|
|
/// Allows interested objects to be notified when this property changes.
|
|
/// </devdoc>
|
|
public override void AddValueChanged(object component, EventHandler handler) {
|
|
if (component == null) throw new ArgumentNullException("component");
|
|
if (handler == null) throw new ArgumentNullException("handler");
|
|
|
|
EventInfo changedEvent = ChangedEventValue;
|
|
if (changedEvent != null) {
|
|
changedEvent.AddEventHandler(component, handler);
|
|
}
|
|
else {
|
|
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)GetDebugInvokee(componentClass, provider);
|
|
return (bool)shouldSerialize.Invoke(provider, new object[] { component});
|
|
}
|
|
catch {}
|
|
}
|
|
}
|
|
else {
|
|
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)GetDebugInvokee(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)GetDebugInvokee(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)GetDebugInvokee(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)GetDebugInvokee(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));
|
|
}
|
|
|
|
/// <devdoc>
|
|
/// 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.
|
|
/// </devdoc>
|
|
public override bool CanResetValue(object component) {
|
|
if (IsExtender) {
|
|
return false;
|
|
}
|
|
|
|
if (DefaultValue != noValue) {
|
|
return !object.Equals(GetValue(component),DefaultValue);
|
|
}
|
|
|
|
if (ResetMethodValue != null) {
|
|
if (ShouldSerializeMethodValue != null) {
|
|
component = GetDebugInvokee(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 three 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 of the property itself, from base class to most derived. This way
|
|
// derived class attributes replace base class attributes.
|
|
//
|
|
// 3. 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 DebugTypeDescriptor.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;
|
|
object[][] attributeStack = new object[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) {
|
|
memberInfo = currentReflectType.GetMethod("Get" + Name, bindingFlags);
|
|
}
|
|
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] = DebugTypeDescriptor.GetCustomAttributes(memberInfo);
|
|
}
|
|
|
|
// Ready for the next loop iteration.
|
|
//
|
|
currentReflectType = currentReflectType.BaseType;
|
|
}
|
|
|
|
// Now trawl the attribute stack so that we add attributes
|
|
// from base class to most derived.
|
|
//
|
|
foreach(object[] attributeArray in attributeStack) {
|
|
if (attributeArray != null) {
|
|
foreach(object attr in attributeArray) {
|
|
if (attr is Attribute) {
|
|
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 (!state[BitReadOnlyChecked]) {
|
|
state[BitReadOnlyChecked] = true;
|
|
if (SetMethodValue == null) {
|
|
attributes.Add(ReadOnlyAttribute.Yes);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <devdoc>
|
|
/// Retrieves the properties
|
|
/// </devdoc>
|
|
public override PropertyDescriptorCollection GetChildProperties(object instance, Attribute[] filter) {
|
|
if (instance == null) {
|
|
return DebugTypeDescriptor.GetProperties(PropertyType, filter);
|
|
}
|
|
else {
|
|
return DebugTypeDescriptor.GetProperties(instance, filter);
|
|
}
|
|
}
|
|
|
|
/// <devdoc>
|
|
/// <para>
|
|
/// Gets
|
|
/// the component
|
|
/// that a method should be invoked on.
|
|
/// </para>
|
|
/// </devdoc>
|
|
private static object GetDebugInvokee(Type componentClass, object component) {
|
|
|
|
// We delve into the component's designer only if it is a component and if
|
|
// the component we've been handed is not an instance of this property type.
|
|
//
|
|
if (!componentClass.IsInstanceOfType(component) && component is IComponent) {
|
|
ISite site = ((IComponent)component).Site;
|
|
if (site != null && site.DesignMode) {
|
|
IDesignerHost host = (IDesignerHost)site.GetService(typeof(IDesignerHost));
|
|
if (host != null) {
|
|
object designer = host.GetDesigner((IComponent)component);
|
|
|
|
// We only use the designer if it has a compatible class. If we
|
|
// got here, we're probably hosed because the user just passed in
|
|
// an object that this PropertyDescriptor can't munch on, but it's
|
|
// clearer to use that object instance instead of it's designer.
|
|
//
|
|
if (designer != null && componentClass.IsInstanceOfType(designer)) {
|
|
component = designer;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Debug.Assert(component != null, "Attempt to invoke on null component");
|
|
return component;
|
|
}
|
|
|
|
/// <devdoc>
|
|
/// <para>
|
|
/// Gets an editor of the specified type.
|
|
/// </para>
|
|
/// </devdoc>
|
|
public override object GetEditor(Type editorBaseType) {
|
|
object editor = null;
|
|
|
|
// Check the editors we've already created for this type.
|
|
//
|
|
if (editorTypes != null) {
|
|
for (int i = 0; i < editorCount; i++) {
|
|
if (editorTypes[i] == editorBaseType) {
|
|
return editors[i];
|
|
}
|
|
}
|
|
}
|
|
|
|
// If one wasn't found, then we must go through the attributes.
|
|
//
|
|
if (editor == null) {
|
|
for (int i = 0; i < Attributes.Count; i++) {
|
|
|
|
if (!(Attributes[i] is EditorAttribute)) {
|
|
continue;
|
|
}
|
|
|
|
EditorAttribute attr = (EditorAttribute)Attributes[i];
|
|
Type editorType = GetTypeFromName(attr.EditorBaseTypeName);
|
|
|
|
if (editorBaseType == editorType) {
|
|
Type type = GetTypeFromName(attr.EditorTypeName);
|
|
if (type != null) {
|
|
editor = CreateInstance(type);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Now, if we failed to find it in our own attributes, go to the
|
|
// component descriptor.
|
|
//
|
|
if (editor == null) {
|
|
editor = DebugTypeDescriptor.GetEditor(PropertyType, editorBaseType);
|
|
}
|
|
|
|
// Now, another slot in our editor cache for next time
|
|
//
|
|
if (editorTypes == null) {
|
|
editorTypes = new Type[5];
|
|
editors = new object[5];
|
|
}
|
|
|
|
if (editorCount >= editorTypes.Length) {
|
|
Type[] newTypes = new Type[editorTypes.Length * 2];
|
|
object[] newEditors = new object[editors.Length * 2];
|
|
Array.Copy(editorTypes, newTypes, editorTypes.Length);
|
|
Array.Copy(editors, newEditors, editors.Length);
|
|
editorTypes = newTypes;
|
|
editors = newEditors;
|
|
}
|
|
|
|
editorTypes[editorCount] = editorBaseType;
|
|
editors[editorCount++] = editor;
|
|
}
|
|
|
|
return editor;
|
|
}
|
|
|
|
/// <devdoc>
|
|
/// Retrieves the current value of the property on component,
|
|
/// invoking the getXXX method. An exception in the getXXX
|
|
/// method will pass through.
|
|
/// </devdoc>
|
|
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 = GetDebugInvokee(componentClass, component);
|
|
|
|
|
|
try {
|
|
return GetMethodValue.Invoke(component, null);
|
|
}
|
|
catch (Exception t) {
|
|
|
|
string name = null;
|
|
if (component is IComponent) {
|
|
|
|
ISite site = ((IComponent)component).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;
|
|
}
|
|
|
|
/// <devdoc>
|
|
/// This should be called by your property descriptor implementation
|
|
/// when the property value has changed.
|
|
/// </devdoc>
|
|
protected override void OnValueChanged(object component, EventArgs e) {
|
|
if (state[BitChangedQueried] && realChangedEventInfo == null) {
|
|
base.OnValueChanged(component, e);
|
|
}
|
|
}
|
|
|
|
/// <devdoc>
|
|
/// Allows interested objects to be notified when this property changes.
|
|
/// </devdoc>
|
|
public override void RemoveValueChanged(object component, EventHandler handler) {
|
|
if (component == null) throw new ArgumentNullException("component");
|
|
if (handler == null) throw new ArgumentNullException("handler");
|
|
|
|
EventInfo changedEvent = ChangedEventValue;
|
|
if (changedEvent != null) {
|
|
changedEvent.RemoveEventHandler(component, handler);
|
|
}
|
|
else {
|
|
base.RemoveValueChanged(component, handler);
|
|
}
|
|
}
|
|
|
|
/// <devdoc>
|
|
/// 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.
|
|
/// </devdoc>
|
|
public override void ResetValue(object component) {
|
|
object invokee = GetDebugInvokee(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) {
|
|
oldValue = GetMethodValue.Invoke(invokee, (object[])null);
|
|
try {
|
|
changeService.OnComponentChanging(component, this);
|
|
}
|
|
catch (CheckoutException coEx) {
|
|
if (coEx == CheckoutException.Canceled) {
|
|
return;
|
|
}
|
|
throw coEx;
|
|
}
|
|
catch {
|
|
throw;
|
|
}
|
|
|
|
}
|
|
|
|
if (ResetMethodValue != null) {
|
|
ResetMethodValue.Invoke(invokee, (object[])null);
|
|
|
|
// Now notify the change service that the change was successful.
|
|
//
|
|
if (changeService != null) {
|
|
newValue = GetMethodValue.Invoke(invokee, (object[])null);
|
|
changeService.OnComponentChanged(component, this, oldValue, newValue);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <devdoc>
|
|
/// 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.
|
|
/// </devdoc>
|
|
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 = GetDebugInvokee(componentClass, component);
|
|
|
|
Debug.Assert(!IsReadOnly, "SetValue attempted on read-only property [" + Name + "]");
|
|
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 = GetMethodValue.Invoke(invokee, null);
|
|
try {
|
|
changeService.OnComponentChanging(component, this);
|
|
}
|
|
catch (CheckoutException coEx) {
|
|
if (coEx == CheckoutException.Canceled) {
|
|
return;
|
|
}
|
|
throw coEx;
|
|
}
|
|
}
|
|
|
|
try {
|
|
try {
|
|
SetMethodValue.Invoke(invokee, new object[]{value});
|
|
OnValueChanged(invokee, EventArgs.Empty);
|
|
}
|
|
catch (Exception t) {
|
|
// Give ourselves a chance to unwind properly before rethrowing the exception (bug# 20221).
|
|
//
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <devdoc>
|
|
/// 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 DebugReflectPropertyDescriptor, 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.
|
|
/// </devdoc>
|
|
public override bool ShouldSerializeValue(object component) {
|
|
|
|
component = GetDebugInvokee(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));
|
|
}
|
|
|
|
|
|
/*
|
|
The following code has been removed to fix FXCOP violations. The code
|
|
is left here incase it needs to be resurrected in the future.
|
|
|
|
/// <devdoc>
|
|
/// A constructor for ReflectPropertyDescriptors that have no attributes.
|
|
/// </devdoc>
|
|
public DebugReflectPropertyDescriptor(Type componentClass, string name, Type type) : this(componentClass, name, type, (Attribute[])null) {
|
|
}
|
|
*/
|
|
}
|
|
}
|
|
|
|
#endif
|