How do I dynamically generate columns in a WPF DataGrid?

后端 未结 4 1720
一生所求
一生所求 2020-12-01 00:55

I am attempting to display the results of a query in a WPF datagrid. The ItemsSource type I am binding to is IEnumerable. As the fields returned

4条回答
  •  甜味超标
    2020-12-01 01:29

    Although there is an accepted answer by the OP, it uses AutoGenerateColumns="False" which is not exactly what the original question asked for. Fortunately, it can be solved with auto-generated columns as well. The key to the solution is the DynamicObject that can have both static and dynamic properties:

    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 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 () {
          result = 
          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 () {
           = value;
          return true;
        }
        else
          return false;
      }
    
      public PropertyDescriptorCollection GetProperties() {
        // This is where we assemble *all* properties:
        var collection = new List();
        // 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;
    }
    

    For the ICustomTypeDescriptor implementation, you can mostly use the static functions of TypeDescriptor in a trivial manner. GetProperties() is the one that requires real implementation: reading the existing properties and adding your dynamic ones.

    As PropertyDescriptor is abstract, you have to inherit it:

    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) {
        ...
      }
    

提交回复
热议问题