Changing property attributes at runtime

K

Kimmo Laine

Hi is there a way to change propertys attribute from the code?

Let´s say that i have the following property in my class:

[DisplayName("Number of cars")]
public int Count[
get { . . . }
set { . . . ]
}

Is there a way to change the displayname, from my code, at runtime to
"Number of bikes".

thx!

Kimmo Laine
 
M

Marc Gravell

Yes; kind of... but not trivial... I guess it comes down to "why?" as to
whether it is worth the effort... are you using this on a property grid or
something? Because if this is just for a column heading on a grid, I suspect
you can override that text on the grid control...

You could try just obtaining the reflective attribute via GetType and
GetProperty, but IIRC this doesn't "stick".

However, for the /component/ model (which Forms binding uses) you can
actually do pretty much anything... invent properties on the fly, rename
them, change the attributes, etc:
You can implement the ICustomTypeDescriptor interface; forward most of the
methods to the TypeDescriptor equivalents (using GetType(), not "this" as
the parameter); for GetProperties, call the TypeDescriptor version, but then
change the results for the property you care about... instead of returning
the (reflective) PropertyDescriptor you get, you should be able to return a
bespoke one that changes the Attributes.

Too much there for a short example, but I have useed the above (as part of a
complex system) with great success (for dynamic properties similar to
DataSet columnns).

Marc
 
G

Guest

Try this may require a little tweaking but should be close:

using System;
using System.Reflection;

// A custom attribute to allow a target to have a display value.
public class DisplayName: Attribute {
// The constructor is called when the attribute is set.
public AnimalTypeAttribute(String Value) {
_value = Value;
}

// Keep a variable internally ...
protected string _value;

// .. and show a copy to the outside world.
public string Value {
get { return _value; }
set { _value = value; }
}
}

// A test class where property has its own display value.
class DisplayNameTestClass {
[DisplayName("Number of cars")]
public int Count
{
get { . . . }
set { . . . }
}

}

class DemoClass {
static void Main(string[] args) {
DisplayNameTestClass testClass = new DisplayNameTestClass
Type type = testClass.GetType();
FieldInfo myFieldInfo = type.GetField("Count")
DisplayName att =
(DisplayName)Attribute.GetCustomAttribute(myFieldInfo,Type.GetType("DisplayName"));
att.Value = "Number of bikes";
}
}
 
M

Marc Gravell

Well, to my mind, the main use of the [DisplayName] is for the component
model - otherwise the OP could just use a static property of
"CountDisplayName" or similar and read it at runtime.

For this to be useful, it would have to return the correct value to the
following (which is what 99% of callers will be using):

string displayName = // wrap
TypeDescriptor.GetProperties(testClass)["Count"].DisplayName;

The code posted won't satisfy this, since the component model will simply
ignore the new "DisplayName" attribute, using the pucka DisplayNameAttribute
from the System.ComponentModel namespace instead.

Additionally, the DisplayNameAttribute.DisplayName property is readonly, so
you can't tweak it with reflection. In short, I don't think this will behave
as expected.

Marc
 
M

Marc Gravell

OK, here's the full version; obviously you can change other discreet
values via the PropertyDescriptor implementation, or any attribute via
GetAttributes. I have implemented it as a static instance here, but you
could merrily recreate for each instance, or even on each call.
Personally I use an extenstion of this in a dynamic property bag
approach.

Most of the code is boiler-plate; ForwardingPropertyDescriptor just
allows a daisy-chain. I've chucked in a demo to illustrate it working
with a property-grid and data-grid-view.

using System;
using System.ComponentModel;
using System.Collections.Generic;
using System.Windows.Forms;

class Program {
static void Main() {
SomeClass test = new SomeClass();
ShowObject(test, "Before");
SomeClass.CountDisplayName = "Toadstool count";
ShowObject(test, "After");
}
private static void ShowObject(SomeClass item, string title) {
using (Form form = new Form())
using (PropertyGrid propGrid = new PropertyGrid())
using (DataGridView gridView = new DataGridView()) {
form.Height = 500;
form.Text = title;
propGrid.Dock = DockStyle.Fill;
gridView.Dock = DockStyle.Bottom;
form.Controls.Add(propGrid);
form.Controls.Add(gridView);
propGrid.SelectedObject = item;
List<SomeClass> items = new List<SomeClass>();
items.Add(item);
gridView.DataSource = items;
form.ShowDialog();
}
}
}
class SomeClass : ICustomTypeDescriptor {
private int count = 5;
[DisplayName("Count of something"), DefaultValue(5)]
public int Count {
get {return count;}
set {count = value;}
}
private string somethingElse = "Empty";
[DisplayName("Another Property"), DefaultValue("Empty")]
public string SomethingElse {
get { return somethingElse; }
set { somethingElse = value; }
}
AttributeCollection ICustomTypeDescriptor.GetAttributes() {
return TypeDescriptor.GetAttributes(GetType());
}

string ICustomTypeDescriptor.GetClassName() {
return TypeDescriptor.GetClassName(GetType());
}

string ICustomTypeDescriptor.GetComponentName() {
return TypeDescriptor.GetComponentName(GetType());
}

TypeConverter ICustomTypeDescriptor.GetConverter() {
return TypeDescriptor.GetConverter(GetType());
}

EventDescriptor ICustomTypeDescriptor.GetDefaultEvent() {
return TypeDescriptor.GetDefaultEvent(GetType());
}

PropertyDescriptor ICustomTypeDescriptor.GetDefaultProperty() {
return TypeDescriptor.GetDefaultProperty(GetType());
}

object ICustomTypeDescriptor.GetEditor(Type editorBaseType) {
return TypeDescriptor.GetEditor(GetType(), editorBaseType);
}

EventDescriptorCollection
ICustomTypeDescriptor.GetEvents(Attribute[] attributes) {
return TypeDescriptor.GetEvents(GetType(), attributes);
}

EventDescriptorCollection ICustomTypeDescriptor.GetEvents() {
return TypeDescriptor.GetEvents(GetType());
}

PropertyDescriptorCollection
ICustomTypeDescriptor.GetProperties(Attribute[] attributes) {
return HackProperties(TypeDescriptor.GetProperties(GetType(),
attributes));
}

PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties()
{
return HackProperties(TypeDescriptor.GetProperties(GetType()));
}

object ICustomTypeDescriptor.GetPropertyOwner(PropertyDescriptor
pd) {
return this;
}

private PropertyDescriptorCollection
HackProperties(PropertyDescriptorCollection properties) {
List<PropertyDescriptor> propList = new
List<PropertyDescriptor>();
foreach (PropertyDescriptor prop in properties) {
if (prop.Name == "Count")
propList.Add(CountPropertyDescriptor);
else
propList.Add(prop);
}
return new PropertyDescriptorCollection(propList.ToArray(),
true);
}
private static readonly DisplayNamePropertyDescriptor
CountPropertyDescriptor;
public SomeClass() { }
public static string CountDisplayName {
get { return CountPropertyDescriptor.DisplayName; }
set { CountPropertyDescriptor.SetDisplayName(value); }
}
static SomeClass() {
CountPropertyDescriptor = new
DisplayNamePropertyDescriptor(TypeDescriptor.GetProperties(typeof(SomeClass))["Count"]);
}
}
public class DisplayNamePropertyDescriptor :
ForwardingPropertyDescriptor {
private string displayName;
public void SetDisplayName(string displayName) {
this.displayName = displayName;
}
public DisplayNamePropertyDescriptor(PropertyDescriptor root)
: base(root) {
SetDisplayName(root.DisplayName);
}
public override string DisplayName {
get { return displayName; }
}

}

public abstract class ForwardingPropertyDescriptor : PropertyDescriptor
{
private readonly PropertyDescriptor _root;
protected PropertyDescriptor Root { get { return _root; } }
protected ForwardingPropertyDescriptor(PropertyDescriptor root)
: base(root) {
_root = root;
}
public override void AddValueChanged(object component, EventHandler
handler) {
Root.AddValueChanged(component, handler);
}
public override AttributeCollection Attributes {
get {
return Root.Attributes;
}
}
public override bool CanResetValue(object component) {
return Root.CanResetValue(component);
}
public override string Category {
get {
return Root.Category;
}
}
public override Type ComponentType {
get { return Root.ComponentType; }
}
public override TypeConverter Converter {
get {
return Root.Converter;
}
}
public override string Description {
get {
return Root.Description;
}
}
public override bool DesignTimeOnly {
get {
return Root.DesignTimeOnly;
}
}
public override string DisplayName {
get {
return Root.DisplayName;
}
}
public override bool Equals(object obj) {
return Root.Equals(obj);
}
public override PropertyDescriptorCollection
GetChildProperties(object instance, Attribute[] filter) {
return Root.GetChildProperties(instance, filter);
}
public override object GetEditor(Type editorBaseType) {
return Root.GetEditor(editorBaseType);
}
public override int GetHashCode() {
return Root.GetHashCode();
}
public override object GetValue(object component) {
return Root.GetValue(component);
}
public override bool IsBrowsable {
get {
return Root.IsBrowsable;
}
}
public override bool IsLocalizable {
get {
return Root.IsLocalizable;
}
}
public override bool IsReadOnly {
get { return Root.IsReadOnly; }
}
public override string Name {
get {
return Root.Name;
}
}
public override Type PropertyType {
get { return Root.PropertyType; }
}
public override void RemoveValueChanged(object component,
EventHandler handler) {
Root.RemoveValueChanged(component, handler);
}
public override void ResetValue(object component) {
Root.ResetValue(component);
}
public override void SetValue(object component, object value) {
Root.SetValue(component, value);
}
public override bool ShouldSerializeValue(object component) {
return Root.ShouldSerializeValue(component);
}
public override bool SupportsChangeEvents {
get {
return Root.SupportsChangeEvents;
}
}
public override string ToString() {
return Root.ToString();
}
}
 
K

Kimmo Laine

Thx for your replys! I used the Marc´s method and did the following:

// Data which is shown in the propertygrid. It inherits from my custom
descriptor
class MyData : MyCustomDescriptor {
// Property which i need to localize. Custom attribute holds the string
ID which is loaded from the resource file
[MyAttribute(123)]
public int Count{ . . . }
}

// Custom descriptor. Used when propertygrid is inspecting my class - MyData
class MyCustomDescriptor : ICustomTypeDescriptor {
public PropertyDescriptorCollection GetProperties() {
PropertyDescriptorCollection orgPdc =
TypeDescriptor.GetProperties(this, true);
PropertyDescriptorCollection newPdc = new
PropertyDescriptorCollection(null);

foreach(PropertyDescriptor t in orgPdc) {
// See if the property name needs to be changed
MyAttribute ma = (MyAttribute)t.Attributes[typeof(MyAttribute)];
if (ma != null) {
// Create custom description object - explained later
newPdc.Add(new MyDescription(t, ma.MsgID));
} else {
// Use normal
newPdc.Add(t);
}
}

return newPdc;
}
}

// Custom description object - the actual work is done here
class MyDescription : PropertyDescriptor {
private PropertyDescriptor m_PD = null;
private int m_MsgID = 0;

public MyDescription(PropertyDescriptor pd, int msgID) : base(pd)
{
m_PD = pd;
m_MsgID = msgID;
}

public override string DisplayName {
get {
return // Load the resource (m_MsgID) from somewhere
}
}
}


-K
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top