a575963da9
Former-commit-id: da6be194a6b1221998fc28233f2503bd61dd9d14
379 lines
12 KiB
C#
379 lines
12 KiB
C#
//
|
|
// System.ComponentModel.PropertyDescriptor.cs
|
|
//
|
|
// Author:
|
|
// Lluis Sanchez Gual (lluis@ximian.com)
|
|
// Ivan N. Zlatev (contact i-nZ.net)
|
|
// (C) Novell, Inc.
|
|
//
|
|
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining
|
|
// a copy of this software and associated documentation files (the
|
|
// "Software"), to deal in the Software without restriction, including
|
|
// without limitation the rights to use, copy, modify, merge, publish,
|
|
// distribute, sublicense, and/or sell copies of the Software, and to
|
|
// permit persons to whom the Software is furnished to do so, subject to
|
|
// the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be
|
|
// included in all copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
//
|
|
|
|
using System;
|
|
using System.Collections;
|
|
using System.Reflection;
|
|
using System.Runtime.InteropServices;
|
|
using System.ComponentModel.Design;
|
|
|
|
namespace System.ComponentModel
|
|
{
|
|
internal class ReflectionPropertyDescriptor : PropertyDescriptor
|
|
{
|
|
PropertyInfo _member;
|
|
Type _componentType;
|
|
Type _propertyType;
|
|
PropertyInfo getter, setter;
|
|
bool accessors_inited;
|
|
|
|
public ReflectionPropertyDescriptor (Type componentType, PropertyDescriptor oldPropertyDescriptor, Attribute [] attributes)
|
|
: base (oldPropertyDescriptor, attributes)
|
|
{
|
|
_componentType = componentType;
|
|
_propertyType = oldPropertyDescriptor.PropertyType;
|
|
}
|
|
|
|
public ReflectionPropertyDescriptor (Type componentType, string name, Type type, Attribute [] attributes)
|
|
: base (name, attributes)
|
|
{
|
|
_componentType = componentType;
|
|
_propertyType = type;
|
|
}
|
|
|
|
public ReflectionPropertyDescriptor (PropertyInfo info)
|
|
: base (info.Name, null)
|
|
{
|
|
_member = info;
|
|
_componentType = _member.DeclaringType;
|
|
_propertyType = info.PropertyType;
|
|
}
|
|
|
|
PropertyInfo GetPropertyInfo ()
|
|
{
|
|
if (_member == null) {
|
|
_member = _componentType.GetProperty (Name, BindingFlags.GetProperty | BindingFlags.NonPublic |
|
|
BindingFlags.Public | BindingFlags.Instance,
|
|
null, this.PropertyType,
|
|
new Type[0], new ParameterModifier[0]);
|
|
if (_member == null)
|
|
throw new ArgumentException ("Accessor methods for the " + Name + " property are missing");
|
|
}
|
|
return _member;
|
|
}
|
|
|
|
public override Type ComponentType {
|
|
get { return _componentType; }
|
|
}
|
|
|
|
public override bool IsReadOnly {
|
|
get {
|
|
ReadOnlyAttribute attrib = ((ReadOnlyAttribute) Attributes[typeof (ReadOnlyAttribute)]);
|
|
return !GetPropertyInfo ().CanWrite || attrib.IsReadOnly;
|
|
}
|
|
}
|
|
|
|
public override Type PropertyType {
|
|
get { return _propertyType; }
|
|
}
|
|
|
|
// The last added to the list attributes have higher precedence
|
|
//
|
|
protected override void FillAttributes (IList attributeList)
|
|
{
|
|
base.FillAttributes (attributeList);
|
|
|
|
if (!GetPropertyInfo ().CanWrite)
|
|
attributeList.Add (ReadOnlyAttribute.Yes);
|
|
|
|
// PropertyDescriptor merges the attributes of both virtual and also "new" properties
|
|
// in the the component type hierarchy.
|
|
//
|
|
int numberOfBaseTypes = 0;
|
|
Type baseType = this.ComponentType;
|
|
while (baseType != null && baseType != typeof (object)) {
|
|
numberOfBaseTypes++;
|
|
baseType = baseType.BaseType;
|
|
}
|
|
|
|
Attribute[][] hierarchyAttributes = new Attribute[numberOfBaseTypes][];
|
|
baseType = this.ComponentType;
|
|
while (baseType != null && baseType != typeof (object)) {
|
|
PropertyInfo property = baseType.GetProperty (Name, BindingFlags.NonPublic |
|
|
BindingFlags.Public | BindingFlags.Instance |
|
|
BindingFlags.DeclaredOnly,
|
|
null, this.PropertyType,
|
|
new Type[0], new ParameterModifier[0]);
|
|
if (property != null) {
|
|
object[] attrObjects = property.GetCustomAttributes (false);
|
|
Attribute[] attrsArray = new Attribute[attrObjects.Length];
|
|
attrObjects.CopyTo (attrsArray, 0);
|
|
// add in reverse order so that the base types have lower precedence
|
|
hierarchyAttributes[--numberOfBaseTypes] = attrsArray;
|
|
}
|
|
baseType = baseType.BaseType;
|
|
}
|
|
|
|
foreach (Attribute[] attrArray in hierarchyAttributes) {
|
|
if (attrArray != null) {
|
|
foreach (Attribute attr in attrArray)
|
|
attributeList.Add (attr);
|
|
}
|
|
}
|
|
|
|
foreach (Attribute attribute in TypeDescriptor.GetAttributes (PropertyType))
|
|
attributeList.Add (attribute);
|
|
}
|
|
|
|
public override object GetValue (object component)
|
|
{
|
|
component = MemberDescriptor.GetInvokee (_componentType, component);
|
|
InitAccessors ();
|
|
return getter.GetValue (component, null);
|
|
}
|
|
|
|
DesignerTransaction CreateTransaction (object obj, string description)
|
|
{
|
|
IComponent com = obj as IComponent;
|
|
if (com == null || com.Site == null)
|
|
return null;
|
|
|
|
IDesignerHost dh = (IDesignerHost) com.Site.GetService (typeof(IDesignerHost));
|
|
if (dh == null)
|
|
return null;
|
|
|
|
DesignerTransaction tran = dh.CreateTransaction (description);
|
|
IComponentChangeService ccs = (IComponentChangeService) com.Site.GetService (typeof(IComponentChangeService));
|
|
if (ccs != null)
|
|
ccs.OnComponentChanging (com, this);
|
|
return tran;
|
|
}
|
|
|
|
void EndTransaction (object obj, DesignerTransaction tran, object oldValue, object newValue, bool commit)
|
|
{
|
|
if (tran == null) {
|
|
// FIXME: EventArgs might be differen type.
|
|
OnValueChanged (obj, new PropertyChangedEventArgs (Name));
|
|
return;
|
|
}
|
|
|
|
if (commit) {
|
|
IComponent com = obj as IComponent;
|
|
IComponentChangeService ccs = (IComponentChangeService) com.Site.GetService (typeof(IComponentChangeService));
|
|
if (ccs != null)
|
|
ccs.OnComponentChanged (com, this, oldValue, newValue);
|
|
tran.Commit ();
|
|
// FIXME: EventArgs might be differen type.
|
|
OnValueChanged (obj, new PropertyChangedEventArgs (Name));
|
|
} else
|
|
tran.Cancel ();
|
|
}
|
|
|
|
/*
|
|
This method exists because reflection is way too low level for what we need.
|
|
A given virtual property that is partially overriden by a child won't show the
|
|
non-overriden accessor in PropertyInfo. IOW:
|
|
class Parent {
|
|
public virtual string Prop { get; set; }
|
|
}
|
|
class Child : Parent {
|
|
public override string Prop {
|
|
get { return "child"; }
|
|
}
|
|
}
|
|
PropertyInfo pi = typeof (Child).GetProperty ("Prop");
|
|
pi.GetGetMethod (); //returns the MethodInfo for the overridden getter
|
|
pi.GetSetMethod (); //returns null as no override exists
|
|
*/
|
|
void InitAccessors () {
|
|
if (accessors_inited)
|
|
return;
|
|
PropertyInfo prop = GetPropertyInfo ();
|
|
MethodInfo setterMethod, getterMethod;
|
|
setterMethod = prop.GetSetMethod (true);
|
|
getterMethod = prop.GetGetMethod (true);
|
|
|
|
if (getterMethod != null)
|
|
getter = prop;
|
|
|
|
if (setterMethod != null)
|
|
setter = prop;
|
|
|
|
|
|
if (setterMethod != null && getterMethod != null) {//both exist
|
|
accessors_inited = true;
|
|
return;
|
|
}
|
|
if (setterMethod == null && getterMethod == null) {//neither exist, this is a broken property
|
|
accessors_inited = true;
|
|
return;
|
|
}
|
|
|
|
//In order to detect that this is a virtual property with override, we check the non null accessor
|
|
MethodInfo mi = getterMethod != null ? getterMethod : setterMethod;
|
|
|
|
if (mi == null || !mi.IsVirtual || (mi.Attributes & MethodAttributes.NewSlot) == MethodAttributes.NewSlot) {
|
|
accessors_inited = true;
|
|
return;
|
|
}
|
|
|
|
Type type = _componentType.BaseType;
|
|
while (type != null && type != typeof (object)) {
|
|
prop = type.GetProperty (Name, BindingFlags.GetProperty | BindingFlags.NonPublic |
|
|
BindingFlags.Public | BindingFlags.Instance,
|
|
null, this.PropertyType,
|
|
new Type[0], new ParameterModifier[0]);
|
|
if (prop == null) //nothing left to search
|
|
break;
|
|
if (setterMethod == null)
|
|
setterMethod = mi = prop.GetSetMethod ();
|
|
else
|
|
getterMethod = mi = prop.GetGetMethod ();
|
|
|
|
if (getterMethod != null && getter == null)
|
|
getter = prop;
|
|
|
|
if (setterMethod != null && setter == null)
|
|
setter = prop;
|
|
|
|
if (mi != null)
|
|
break;
|
|
type = type.BaseType;
|
|
}
|
|
accessors_inited = true;
|
|
}
|
|
|
|
public override void SetValue (object component, object value)
|
|
{
|
|
DesignerTransaction tran = CreateTransaction (component, "Set Property '" + Name + "'");
|
|
|
|
object propertyHolder = MemberDescriptor.GetInvokee (_componentType, component);
|
|
object old = GetValue (propertyHolder);
|
|
|
|
try {
|
|
InitAccessors ();
|
|
setter.SetValue (propertyHolder, value, null);
|
|
EndTransaction (component, tran, old, value, true);
|
|
} catch {
|
|
EndTransaction (component, tran, old, value, false);
|
|
throw;
|
|
}
|
|
}
|
|
|
|
MethodInfo FindPropertyMethod (object o, string method_name)
|
|
{
|
|
MethodInfo mi = null;
|
|
string name = method_name + Name;
|
|
|
|
foreach (MethodInfo m in o.GetType().GetMethods (BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public)) {
|
|
// XXX should we really not check the return type of the method?
|
|
if (m.Name == name && m.GetParameters().Length == 0) {
|
|
mi = m;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return mi;
|
|
}
|
|
|
|
public override void ResetValue (object component)
|
|
{
|
|
object propertyHolder = MemberDescriptor.GetInvokee (_componentType, component);
|
|
|
|
DefaultValueAttribute attrib = ((DefaultValueAttribute) Attributes[typeof (DefaultValueAttribute)]);
|
|
if (attrib != null)
|
|
SetValue (propertyHolder, attrib.Value);
|
|
|
|
DesignerTransaction tran = CreateTransaction (component, "Reset Property '" + Name + "'");
|
|
object old = GetValue (propertyHolder);
|
|
|
|
try {
|
|
MethodInfo mi = FindPropertyMethod (propertyHolder, "Reset");
|
|
if (mi != null)
|
|
mi.Invoke (propertyHolder, null);
|
|
EndTransaction (component, tran, old, GetValue (propertyHolder), true);
|
|
} catch {
|
|
EndTransaction (component, tran, old, GetValue (propertyHolder), false);
|
|
throw;
|
|
}
|
|
}
|
|
|
|
public override bool CanResetValue (object component)
|
|
{
|
|
component = MemberDescriptor.GetInvokee (_componentType, component);
|
|
|
|
DefaultValueAttribute attrib = ((DefaultValueAttribute) Attributes[typeof (DefaultValueAttribute)]);
|
|
if (attrib != null) {
|
|
object current = GetValue (component);
|
|
if (attrib.Value == null || current == null){
|
|
if (attrib.Value != current)
|
|
return true;
|
|
if (attrib.Value == null && current == null)
|
|
return false;
|
|
}
|
|
|
|
return !attrib.Value.Equals (current);
|
|
} else {
|
|
if (!_member.CanWrite)
|
|
return false;
|
|
|
|
MethodInfo mi = FindPropertyMethod (component, "ShouldPersist");
|
|
if (mi != null)
|
|
return (bool) mi.Invoke (component, null);
|
|
|
|
mi = FindPropertyMethod (component, "ShouldSerialize");
|
|
if (mi != null && !((bool) mi.Invoke (component, null)))
|
|
return false;
|
|
|
|
mi = FindPropertyMethod (component, "Reset");
|
|
return mi != null;
|
|
}
|
|
}
|
|
|
|
public override bool ShouldSerializeValue (object component)
|
|
{
|
|
component = MemberDescriptor.GetInvokee (_componentType, component);
|
|
|
|
if (IsReadOnly) {
|
|
MethodInfo mi = FindPropertyMethod (component, "ShouldSerialize");
|
|
if (mi != null)
|
|
return (bool) mi.Invoke (component, null);
|
|
return Attributes.Contains (DesignerSerializationVisibilityAttribute.Content);
|
|
}
|
|
|
|
DefaultValueAttribute attrib = ((DefaultValueAttribute) Attributes[typeof (DefaultValueAttribute)]);
|
|
if (attrib != null) {
|
|
object current = GetValue (component);
|
|
if (attrib.Value == null || current == null)
|
|
return attrib.Value != current;
|
|
return !attrib.Value.Equals (current);
|
|
}
|
|
else {
|
|
MethodInfo mi = FindPropertyMethod (component, "ShouldSerialize");
|
|
if (mi != null)
|
|
return (bool) mi.Invoke (component, null);
|
|
// MSDN: If this method cannot find a DefaultValueAttribute or a ShouldSerializeMyProperty method,
|
|
// it cannot create optimizations and it returns true.
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|