WPF DataGrid doesn't generate columns for ICustomTypeDescriptor Properties (but Winforms DataGridView does)

1.7k Views Asked by At

As in the title, I have a DataGrid and a ViewModel that implements ICustomTypeDescriptor, adding a few properties at runtime.

public class PartCloneSettingsController : BaseController, ICustomTypeDescriptor
{ 
...
    private List<StringPropertyDescriptor> generatedProperties; 

    private void generateProperties()
    {
        foreach (PartAttributeDefinition item in PartAttributes.DefinedAttributes)
        {
            var propertyDescriptor = new StringPropertyDescriptor(item.AttributeTitle, typeof(PartCloneSettingsController), item);

            // attach value changed handler [ memory leak? TODO: Remove handler at some point...]
            propertyDescriptor.AddValueChanged(this, OnGeneratedPropertyChanged);
            generatedProperties.Add(propertyDescriptor);
        }
    }


    public PropertyDescriptorCollection GetProperties()
    {
        // Get All Default (defined) properties
        var properties = TypeDescriptor.GetProperties(this, true);

        // concat default properties and generated properties into a single collection
        var newProperties = new PropertyDescriptorCollection(properties.Cast<PropertyDescriptor>().Concat(generatedProperties).ToArray());

        return newProperties;
    }

}

DataGrid Definition in XAML:

<DataGrid x:Name="dataGrid" AutoGenerateColumns="True"  />

I set the ItemsSource like this:

controller.LoadAssembly(ofd.FileName); // loads the file and creates a ObservableCollection of PartCloneSettingsControllers...

// set data grid source, doesn't create columns for generated properties...
overviewGrid.ItemsSource = controller.PartCloneSettingsControllers

 // set data source from a winforms DataGridView which generates columns properly...
((System.Windows.Forms.DataGridView)wfhost.Child).DataSource =   controller.PartCloneSettingsControllers;

where controller.PartCloneSettingsControllers is defined as:

public ObservableCollection<PartCloneSettingsController> PartCloneSettingsControllers {  get; private set; }

For debugging purposes I created a DataGridView in a Winforms control host and attached the same ViewModel to it, and voila: The Winforms grid creates all columns and displays the data as I want it, but the WPF DataGrid fails to generate columns for my custom properties ( it works with plain, normal properties).

Does anybody have a working solution using a DataGrid, ICustomTypeDescriptor and AutoGenerateColumns=True (if I generate the columns by hand in XAML it works fine, and I can bind to all my properties...)

1

There are 1 best solutions below

3
On BEST ANSWER

If your PartCloneSettingsControllers is a generic collection of some item type other than object, it will use the generic parameter type to populate the columns, as opposed to the runtime type of the items in the collection.

For example, if your collection is an IEnumerable<PartCloneSettingsController>, then the grid will only populate columns for the properties declared on the PartCloneSettingsController type (and its base types)1. It will not bother inspecting the actual objects in the collection, and since ICustomTypeDescriptor exposes properties at the instance level, the grid won't see those properties.

If you have the option of exposing your 'dynamic' properties at the type or collection level instead of the item instance level (e.g., by using TypeDescriptionProvider or ITypedList), that would probably be your best bet. Otherwise, you'll have to use a collection type for which the grid can't infer the item type (e.g., List<object>); that will force the grid to inspect the first item it encounters to figure out what the columns should be.


1 The grid ultimately resolves properties using TypeDescriptor.GetProperties(Type componentType), as opposed to TypeDescriptor.GetProperties(object component), so without an actual item to inspect, it has no way of knowing which 'dynamic' properties are exposed by the individual items.