2

Here is my situation:

I have a 3rd party Component Control which I successfully inherited, and now I am doing some custumization to it.

Part of that customization, is to change the DefaultValues of some properties it has.

So far it all went OK, but this control has some "Sub" Classes in it, that I want to change their propeties' DefaultValues too.

By "Sub" Classes I mean: Let's say my control is called SomeControl, so it has properties in it, but it also has a property that is expandable, like: SomeControl.Rows, The .Rows property returns a RowCollection which has its own properties. So this is what I mean by "Sub" Class (please correct me if there's a better term for this)

In any case, If I want to change some DefaultValues in the RowCollection class, I need to inherit it too. The propblem is that RowCollection's Ctor is internal, so I cannot inherit that class.

So is there any other way for me to change the DefaultValue attribute for a property in a class that I cannot inherit?

Maybe via reflection?

I nee to change it for an instance(object) that I have existing - one instance, and not for the class in general.. (since after 1 object is created from it, no more are created..)

Thank you

Omer
  • 91
  • 2
  • 9
  • You should probably speak about *nested objects* rather than *sub classes*, as that might be confusing with respect to *subclasses* in the sense of child classes. In any case, are you referring to [this `DefaultValue` attribute](http://msdn.microsoft.com/en-us/library/system.componentmodel.defaultvalueattribute%28v=vs.100%29.aspx)? Can you explain why you need to replace it, rather than assigning non-default property values to your one modified instance? – O. R. Mapper Dec 07 '14 at 13:44
  • 1) OK then, Nested Objects. Yes I was aware that the term Sub Classing might be confusing, that's why I put the "Sub" part of it in parenthesis. (The old "Subclassing" term was replaced by the term Inheritance) – Omer Dec 07 '14 at 13:58
  • 1
    2) Yes I am referring to System.ComponentModel.DefaultValueAttribute. In my Inherited class(Control), I am assigning some Non-Default values, in the Ctor of the inherited class, but I want the new values to appear as Default Values, so later on when I use that **Inherited** control in my projects, then I will see the new values as the default, so they will not appear as Bold in the PropertyGrid in VisualStudio.. – Omer Dec 07 '14 at 14:00
  • [DefaultValue] is not capable of this, it only works for simple values. The alternative is to write a private bool ShouldSerializeXxx() method where Xxx is the property name that exposes the row collection. And provide a ResetXxx() method to allow the use of the Reset command. – Hans Passant Dec 07 '14 at 14:54
  • Hans I think you misunderstood my question. You wrote "[DefaultValue] is not capable of this, it only works for simple values." - but I do want to apply it for a simple value. That simple value is one of the RowCollection properties. – Omer Dec 07 '14 at 17:35

1 Answers1

2

Summary: I don't think it is possible to do this via reflection, but here is how I would do it:

First get the property you want:

var defValAttr = typeof (SomeControl.RowCollection)
     .GetProperty("yourProperty")
     .GetCustomAttributes(typeof(DefaultValueAttribute), false)
     .First()
     as DefaultValueAttribute;

DefaultValueAttribute has a .Value property, which is readonly. To modify it you need reflection again to change the private .value field:

var valueField= typeof (DefaultValueAttribute)
     .GetField("value", BindingFlags.NonPublic 
                      | BindingFlags.GetField 
                      | BindingFlags.Instance);

valueField.SetValue(defValAttr, yourNewDefaultValue);

I have not tested this myself, but as far as I know this will not work. The objects returned by GetCustomAttributes are not the actual attributes; you can get the values of the real attributes this way, but you cannot change them. (see Can attributes be added dynamically in C#?)

However, even if this would work, it would change the DefaultValue for all instances of SomeControl.Rows, whether they are within your custom derived control or in the base control. There is no way to change only the DefaultValue of a single instance, since the this value is stored only once per class, not once per instance. It would be very wasteful if there was a copy of each attribute for each instance of an object.

So in conclusion, I don't think there is a way to do this.

As an alternative, you could decompile the third-party assembly and add your own assembly with InternalsVisibleTo, that way you can access the internal constructor.


Addendum: There may still be a way to do this, depending on what sort of attribute you want to change. The attributes accessible via reflection are as I said probably unchangeable. However The visual studio editor and the whole WPF component model actually uses a TypeDescriptor to manage attributes on top of the basic attribute system built into the language. You may be able to change the attributes used by the TypeProvider, and if code later accesses the attributes via TypeDescriptor, it may see the changed values.

The following is again untested:

var instanceOfRow = instanceOfYourControl.Rows;
var defValAttr = TypeDescriptor
     .GetProperties(instanceOfRow)["yourProperty"]
     .Attributes[typeof (DefaultValueAttribute)]
     as DefaultValueAttribute;

var valueField= typeof (DefaultValueAttribute)
     .GetField("value", BindingFlags.NonPublic 
                      | BindingFlags.GetField 
                      | BindingFlags.Instance);

valueField.SetValue(defValAttr, yourNewDefaultValue);
Community
  • 1
  • 1
HugoRune
  • 13,157
  • 7
  • 69
  • 144
  • Thank you Hugo for this answer. I am trying the code right now, will report soon.. – Omer Dec 07 '14 at 17:36
  • OK I tested it now. It succeeds in changing the value only for the Instance of DefaultValueAttribute that I got, not the instance that the control uses. Exactly as you said.. maybe there's a trick we can do? Maybe we can take that instance that we successfully changed, and then somehow replace the control's instance with that edited instance.. Is that possible? – Omer Dec 07 '14 at 18:11
  • I am not certain about this, but as far as I understand, there is no "correct" instance to change. There is no actual DefaultValueAttribute object for a given class that can be changed, this is just a representation that is generated when you use reflection. The actual default value is stored differently, and not accessible via reflection. – HugoRune Dec 07 '14 at 18:28
  • 1
    @Omer I had another idea: you may not be able to change the original attributes, but you may be able to modify information that the TypeDescriptor provides on top of that, see my edit. – HugoRune Dec 07 '14 at 20:01
  • Thank you Hugo. I learned from this alot. Due to the fact tht this case requires alot of hacking, I will stay with only a part of what I planned: I set the new values that I want in the inheritted control's Ctor, and I set the DefaultValue attribute for the Control's properties, but for properties of the Nested Classes, I don't set the attribute, which means it will appear in Bold in the PropertyGrid, and also will be written to the hidden Designer Code that VS generates.. It's not perfect, yet it at least achieves the goal of changing the properties in the inherited control. – Omer Dec 09 '14 at 14:51