Skip to content

PropertyGrid.SelectedObjects shows empty grid with typed arrays when PropertySort is NoSort (.NET 9/10 regression) #14187

@p5ych08illy

Description

@p5ych08illy

.NET version

10.0.100-preview (also affects .NET 9)

Did it work in .NET Framework?

Yes

Did it work in any of the earlier releases of .NET Core or .NET 5+?

Yes, worked correctly in .NET 8 and earlier.

Issue description

PropertyGrid.SelectedObjects silently fails (shows empty grid) when assigned a typed array (e.g., ItemTypeDescriptor[]) instead of object[] in multi-select scenarios. The internal ArrayTypeMismatchException is swallowed, making this bug difficult to diagnose.

The bug only occurs when PropertySort is set to NoSort.

Root cause: In MultiSelectRootGridEntry.GetMergedProperties(), the NoSort code path calls objects.AsSpan(1). However, Span<T> is not covariant - when AsSpan<object>() is called on an array whose runtime type is MyClass[] (not object[]), it throws ArrayTypeMismatchException.

// In MultiSelectRootGridEntry.GetMergedProperties():
if ((sort & PropertySort.Alphabetical) != 0)
{
    // This path does NOT use AsSpan - works fine
    commonProperties = GetCommonProperties(objects, presort: true, tab, parentEntry);
}
else
{
    // NoSort path - USES AsSpan - FAILS with typed arrays!
    properties = GetCommonProperties(objects.AsSpan(1), presort: true, tab, parentEntry);
    firstProperties = GetCommonProperties(objects.AsSpan(0, 1), presort: false, tab, parentEntry);
}

The root cause can be demonstrated standalone:

object[] arr = new MyClass[] { new MyClass() };  // Covariant assignment - compiles fine
arr.AsSpan();  // Throws ArrayTypeMismatchException!

Workaround: Use object[] explicitly instead of typed arrays:

// Instead of: var descriptors = new ItemTypeDescriptor[] { ... };
var descriptors = new object[] { new ItemTypeDescriptor(...), new ItemTypeDescriptor(...) };
propertyGrid.SelectedObjects = descriptors;

Steps to reproduce

  1. Create a WinForms application targeting .NET 9 or .NET 10
  2. Create a CustomTypeDescriptor-derived class (commonly used for property localization/filtering)
  3. Set PropertyGrid.PropertySort = PropertySort.NoSort
  4. Create a typed array of descriptors and assign to SelectedObjects
  5. Observe: PropertyGrid is empty (no properties shown)
  6. Change to object[] array - properties appear correctly
public class ItemTypeDescriptor : CustomTypeDescriptor
{
    private readonly WeakReference _component;
    public object? Component => _component.Target;

    public ItemTypeDescriptor(object component, ICustomTypeDescriptor parent)
        : base(parent)
    {
        _component = new WeakReference(component);
    }

    public override PropertyDescriptorCollection GetProperties()
        => base.GetProperties();

    public override object? GetPropertyOwner(PropertyDescriptor? pd) 
        => Component;
}

// Setup
propertyGrid.PropertySort = PropertySort.NoSort;

var model1 = new MyModel { Name = "Object1" };
var model2 = new MyModel { Name = "Object2" };

// BUG - typed array results in empty PropertyGrid:
var descriptors = new ItemTypeDescriptor[]
{
    new ItemTypeDescriptor(model1, TypeDescriptor.GetProvider(model1).GetTypeDescriptor(model1)!),
    new ItemTypeDescriptor(model2, TypeDescriptor.GetProvider(model2).GetTypeDescriptor(model2)!)
};
propertyGrid.SelectedObjects = descriptors;  // PropertyGrid shows empty!

// WORKAROUND - object[] works correctly:
var descriptors = new object[]
{
    new ItemTypeDescriptor(model1, TypeDescriptor.GetProvider(model1).GetTypeDescriptor(model1)!),
    new ItemTypeDescriptor(model2, TypeDescriptor.GetProvider(model2).GetTypeDescriptor(model2)!)
};
propertyGrid.SelectedObjects = descriptors;  // Properties are visible!

Minimal repro project attached.

PropertyGridSpanBug.zip

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions