2 // System.ComponentModel.PropertyDescriptor.cs
5 // Lluis Sanchez Gual (lluis@ximian.com)
6 // Ivan N. Zlatev (contact i-nZ.net)
11 // Permission is hereby granted, free of charge, to any person obtaining
12 // a copy of this software and associated documentation files (the
13 // "Software"), to deal in the Software without restriction, including
14 // without limitation the rights to use, copy, modify, merge, publish,
15 // distribute, sublicense, and/or sell copies of the Software, and to
16 // permit persons to whom the Software is furnished to do so, subject to
17 // the following conditions:
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32 using System.Collections;
33 using System.Reflection;
34 using System.Runtime.InteropServices;
35 using System.ComponentModel.Design;
37 namespace System.ComponentModel
39 internal class ReflectionPropertyDescriptor : PropertyDescriptor
44 PropertyInfo getter, setter;
45 bool accessors_inited;
47 public ReflectionPropertyDescriptor (Type componentType, PropertyDescriptor oldPropertyDescriptor, Attribute [] attributes)
48 : base (oldPropertyDescriptor, attributes)
50 _componentType = componentType;
51 _propertyType = oldPropertyDescriptor.PropertyType;
54 public ReflectionPropertyDescriptor (Type componentType, string name, Type type, Attribute [] attributes)
55 : base (name, attributes)
57 _componentType = componentType;
61 public ReflectionPropertyDescriptor (PropertyInfo info)
62 : base (info.Name, null)
65 _componentType = _member.DeclaringType;
66 _propertyType = info.PropertyType;
69 PropertyInfo GetPropertyInfo ()
71 if (_member == null) {
72 _member = _componentType.GetProperty (Name, BindingFlags.GetProperty | BindingFlags.NonPublic |
73 BindingFlags.Public | BindingFlags.Instance,
74 null, this.PropertyType,
75 new Type[0], new ParameterModifier[0]);
77 throw new ArgumentException ("Accessor methods for the " + Name + " property are missing");
82 public override Type ComponentType {
83 get { return _componentType; }
86 public override bool IsReadOnly {
88 ReadOnlyAttribute attrib = ((ReadOnlyAttribute) Attributes[typeof (ReadOnlyAttribute)]);
89 return !GetPropertyInfo ().CanWrite || attrib.IsReadOnly;
93 public override Type PropertyType {
94 get { return _propertyType; }
97 // The last added to the list attributes have higher precedence
99 protected override void FillAttributes (IList attributeList)
101 base.FillAttributes (attributeList);
103 if (!GetPropertyInfo ().CanWrite)
104 attributeList.Add (ReadOnlyAttribute.Yes);
106 // PropertyDescriptor merges the attributes of both virtual and also "new" properties
107 // in the the component type hierarchy.
109 int numberOfBaseTypes = 0;
110 Type baseType = this.ComponentType;
111 while (baseType != null && baseType != typeof (object)) {
113 baseType = baseType.BaseType;
116 Attribute[][] hierarchyAttributes = new Attribute[numberOfBaseTypes][];
117 baseType = this.ComponentType;
118 while (baseType != null && baseType != typeof (object)) {
119 PropertyInfo property = baseType.GetProperty (Name, BindingFlags.NonPublic |
120 BindingFlags.Public | BindingFlags.Instance |
121 BindingFlags.DeclaredOnly,
122 null, this.PropertyType,
123 new Type[0], new ParameterModifier[0]);
124 if (property != null) {
125 object[] attrObjects = property.GetCustomAttributes (false);
126 Attribute[] attrsArray = new Attribute[attrObjects.Length];
127 attrObjects.CopyTo (attrsArray, 0);
128 // add in reverse order so that the base types have lower precedence
129 hierarchyAttributes[--numberOfBaseTypes] = attrsArray;
131 baseType = baseType.BaseType;
134 foreach (Attribute[] attrArray in hierarchyAttributes) {
135 if (attrArray != null) {
136 foreach (Attribute attr in attrArray)
137 attributeList.Add (attr);
141 foreach (Attribute attribute in TypeDescriptor.GetAttributes (PropertyType))
142 attributeList.Add (attribute);
145 public override object GetValue (object component)
147 component = MemberDescriptor.GetInvokee (_componentType, component);
149 return getter.GetValue (component, null);
152 DesignerTransaction CreateTransaction (object obj, string description)
154 IComponent com = obj as IComponent;
155 if (com == null || com.Site == null)
158 IDesignerHost dh = (IDesignerHost) com.Site.GetService (typeof(IDesignerHost));
162 DesignerTransaction tran = dh.CreateTransaction (description);
163 IComponentChangeService ccs = (IComponentChangeService) com.Site.GetService (typeof(IComponentChangeService));
165 ccs.OnComponentChanging (com, this);
169 void EndTransaction (object obj, DesignerTransaction tran, object oldValue, object newValue, bool commit)
172 // FIXME: EventArgs might be differen type.
173 OnValueChanged (obj, new PropertyChangedEventArgs (Name));
178 IComponent com = obj as IComponent;
179 IComponentChangeService ccs = (IComponentChangeService) com.Site.GetService (typeof(IComponentChangeService));
181 ccs.OnComponentChanged (com, this, oldValue, newValue);
183 // FIXME: EventArgs might be differen type.
184 OnValueChanged (obj, new PropertyChangedEventArgs (Name));
190 This method exists because reflection is way too low level for what we need.
191 A given virtual property that is partially overriden by a child won't show the
192 non-overriden accessor in PropertyInfo. IOW:
194 public virtual string Prop { get; set; }
196 class Child : Parent {
197 public override string Prop {
198 get { return "child"; }
201 PropertyInfo pi = typeof (Child).GetProperty ("Prop");
202 pi.GetGetMethod (); //returns the MethodInfo for the overridden getter
203 pi.GetSetMethod (); //returns null as no override exists
205 void InitAccessors () {
206 if (accessors_inited)
208 PropertyInfo prop = GetPropertyInfo ();
209 MethodInfo setterMethod, getterMethod;
210 setterMethod = prop.GetSetMethod (true);
211 getterMethod = prop.GetGetMethod (true);
213 if (getterMethod != null)
216 if (setterMethod != null)
220 if (setterMethod != null && getterMethod != null) {//both exist
221 accessors_inited = true;
224 if (setterMethod == null && getterMethod == null) {//neither exist, this is a broken property
225 accessors_inited = true;
229 //In order to detect that this is a virtual property with override, we check the non null accessor
230 MethodInfo mi = getterMethod != null ? getterMethod : setterMethod;
232 if (mi == null || !mi.IsVirtual || (mi.Attributes & MethodAttributes.NewSlot) == MethodAttributes.NewSlot) {
233 accessors_inited = true;
237 Type type = _componentType.BaseType;
238 while (type != null && type != typeof (object)) {
239 prop = type.GetProperty (Name, BindingFlags.GetProperty | BindingFlags.NonPublic |
240 BindingFlags.Public | BindingFlags.Instance,
241 null, this.PropertyType,
242 new Type[0], new ParameterModifier[0]);
243 if (prop == null) //nothing left to search
245 if (setterMethod == null)
246 setterMethod = mi = prop.GetSetMethod ();
248 getterMethod = mi = prop.GetGetMethod ();
250 if (getterMethod != null && getter == null)
253 if (setterMethod != null && setter == null)
258 type = type.BaseType;
260 accessors_inited = true;
263 public override void SetValue (object component, object value)
265 DesignerTransaction tran = CreateTransaction (component, "Set Property '" + Name + "'");
267 object propertyHolder = MemberDescriptor.GetInvokee (_componentType, component);
268 object old = GetValue (propertyHolder);
272 setter.SetValue (propertyHolder, value, null);
273 EndTransaction (component, tran, old, value, true);
275 EndTransaction (component, tran, old, value, false);
280 MethodInfo FindPropertyMethod (object o, string method_name)
282 MethodInfo mi = null;
283 string name = method_name + Name;
285 foreach (MethodInfo m in o.GetType().GetMethods (BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public)) {
286 // XXX should we really not check the return type of the method?
287 if (m.Name == name && m.GetParameters().Length == 0) {
296 public override void ResetValue (object component)
298 object propertyHolder = MemberDescriptor.GetInvokee (_componentType, component);
300 DefaultValueAttribute attrib = ((DefaultValueAttribute) Attributes[typeof (DefaultValueAttribute)]);
302 SetValue (propertyHolder, attrib.Value);
304 DesignerTransaction tran = CreateTransaction (component, "Reset Property '" + Name + "'");
305 object old = GetValue (propertyHolder);
308 MethodInfo mi = FindPropertyMethod (propertyHolder, "Reset");
310 mi.Invoke (propertyHolder, null);
311 EndTransaction (component, tran, old, GetValue (propertyHolder), true);
313 EndTransaction (component, tran, old, GetValue (propertyHolder), false);
318 public override bool CanResetValue (object component)
320 component = MemberDescriptor.GetInvokee (_componentType, component);
322 DefaultValueAttribute attrib = ((DefaultValueAttribute) Attributes[typeof (DefaultValueAttribute)]);
323 if (attrib != null) {
324 object current = GetValue (component);
325 if (attrib.Value == null || current == null){
326 if (attrib.Value != current)
328 if (attrib.Value == null && current == null)
332 return !attrib.Value.Equals (current);
334 if (!_member.CanWrite)
337 MethodInfo mi = FindPropertyMethod (component, "ShouldPersist");
339 return (bool) mi.Invoke (component, null);
341 mi = FindPropertyMethod (component, "ShouldSerialize");
342 if (mi != null && !((bool) mi.Invoke (component, null)))
345 mi = FindPropertyMethod (component, "Reset");
350 public override bool ShouldSerializeValue (object component)
352 component = MemberDescriptor.GetInvokee (_componentType, component);
355 MethodInfo mi = FindPropertyMethod (component, "ShouldSerialize");
357 return (bool) mi.Invoke (component, null);
358 return Attributes.Contains (DesignerSerializationVisibilityAttribute.Content);
361 DefaultValueAttribute attrib = ((DefaultValueAttribute) Attributes[typeof (DefaultValueAttribute)]);
362 if (attrib != null) {
363 object current = GetValue (component);
364 if (attrib.Value == null || current == null)
365 return attrib.Value != current;
366 return !attrib.Value.Equals (current);
369 MethodInfo mi = FindPropertyMethod (component, "ShouldSerialize");
371 return (bool) mi.Invoke (component, null);
372 // MSDN: If this method cannot find a DefaultValueAttribute or a ShouldSerializeMyProperty method,
373 // it cannot create optimizations and it returns true.