Obwohl es von der OP eine akzeptierte Antwort ist, verwendet es AutoGenerateColumns="False"
, die nicht genau das, was die ursprüngliche Frage gefragt. Glücklicherweise kann es auch mit automatisch generierten Spalten gelöst werden. Der Schlüssel zur Lösung ist die DynamicObject
, die sowohl statische als auch dynamische Eigenschaften haben:
public class MyObject : DynamicObject, ICustomTypeDescriptor {
// The object can have "normal", usual properties if you need them:
public string Property1 { get; set; }
public int Property2 { get; set; }
public MyObject() {
}
public override IEnumerable<string> GetDynamicMemberNames() {
// in addition to the "normal" properties above,
// the object can have some dynamically generated properties
// whose list we return here:
return list_of_dynamic_property_names;
}
public override bool TryGetMember(GetMemberBinder binder, out object result) {
// for each dynamic property, we need to look up the actual value when asked:
if (<binder.Name is a correct name for your dynamic property>) {
result = <whatever data binder.Name means>
return true;
}
else {
result = null;
return false;
}
}
public override bool TrySetMember(SetMemberBinder binder, object value) {
// for each dynamic property, we need to store the actual value when asked:
if (<binder.Name is a correct name for your dynamic property>) {
<whatever storage binder.Name means> = value;
return true;
}
else
return false;
}
public PropertyDescriptorCollection GetProperties() {
// This is where we assemble *all* properties:
var collection = new List<PropertyDescriptor>();
// here, we list all "standard" properties first:
foreach (PropertyDescriptor property in TypeDescriptor.GetProperties(this, true))
collection.Add(property);
// and dynamic ones second:
foreach (string name in GetDynamicMemberNames())
collection.Add(new CustomPropertyDescriptor(name, typeof(property_type), typeof(MyObject)));
return new PropertyDescriptorCollection(collection.ToArray());
}
public PropertyDescriptorCollection GetProperties(Attribute[] attributes) => TypeDescriptor.GetProperties(this, attributes, true);
public AttributeCollection GetAttributes() => TypeDescriptor.GetAttributes(this, true);
public string GetClassName() => TypeDescriptor.GetClassName(this, true);
public string GetComponentName() => TypeDescriptor.GetComponentName(this, true);
public TypeConverter GetConverter() => TypeDescriptor.GetConverter(this, true);
public EventDescriptor GetDefaultEvent() => TypeDescriptor.GetDefaultEvent(this, true);
public PropertyDescriptor GetDefaultProperty() => TypeDescriptor.GetDefaultProperty(this, true);
public object GetEditor(Type editorBaseType) => TypeDescriptor.GetEditor(this, editorBaseType, true);
public EventDescriptorCollection GetEvents() => TypeDescriptor.GetEvents(this, true);
public EventDescriptorCollection GetEvents(Attribute[] attributes) => TypeDescriptor.GetEvents(this, attributes, true);
public object GetPropertyOwner(PropertyDescriptor pd) => this;
}
Für die ICustomTypeDescriptor
Implementierung Sie meist die statischen Funktionen von TypeDescriptor
in trivialer Weise nutzen können. GetProperties()
ist derjenige, der echte Implementierung erfordert: Lesen der vorhandenen Eigenschaften und Hinzufügen Ihrer dynamischen Eigenschaften.
Als PropertyDescriptor
abstrakt ist, muss man es erben:
public class CustomPropertyDescriptor : PropertyDescriptor {
private Type componentType;
public CustomPropertyDescriptor(string propertyName, Type componentType)
: base(propertyName, new Attribute[] { }) {
this.componentType = componentType;
}
public CustomPropertyDescriptor(string propertyName, Type componentType, Attribute[] attrs)
: base(propertyName, attrs) {
this.componentType = componentType;
}
public override bool IsReadOnly => false;
public override Type ComponentType => componentType;
public override Type PropertyType => typeof(property_type);
public override bool CanResetValue(object component) => true;
public override void ResetValue(object component) => SetValue(component, null);
public override bool ShouldSerializeValue(object component) => true;
public override object GetValue(object component) {
return ...;
}
public override void SetValue(object component, object value) {
...
}
Das funktioniert gut, aber wann führen Sie diesen Code aus? Die ItemsSource ist noch nicht festgelegt, wenn Sie dies in DataContextChanged verarbeiten. – Wouter
In meiner Instanz ist ItemSource an eine ViewModel-Eigenschaft gebunden, die als Ergebnis bezeichnet wird. Ich habe einen INotifyPrpertyChanged-Handler in der Ansicht, der darauf reagiert, dass sich diese Eigenschaft ändert. – dkackman
Das war mein Ansatz, aber ich stolperte über ein Problem. Was ist mit Zeilenvalidierung? Mussten Sie die Zeilenvalidierung auf den ExpandoObjects behandeln? – Ninglin