One solution is to subclass the BindingSource. The resulting class will still be a BindingSource, so the designer will know not to put an additional binding source between it and a bound control.
At design time, any property descriptors the designer expects to find will be created on the fly. They will not be remembered. The designer will finish initializing and open and everything will be as expected.
At runtime, the InheritableBindingSource behaves precisely like a BindingSource.
public class InheritableBindingSource : BindingSource
{
public override PropertyDescriptorCollection GetItemProperties(PropertyDescriptor[] listAccessors)
{
if (this.DesignMode)
{
var baseProperties = base.GetItemProperties(listAccessors);
var array = new PropertyDescriptor[baseProperties.Count];
baseProperties.CopyTo(array, 0);
// Return an identical class, but with a modified Find behaviour.
return new DesignerPropertyDescriptorCollection(array);
}
// At runtime, InheritedBindingClass does nothing special.
return base.GetItemProperties(listAccessors);
}
class DesignerPropertyDescriptorCollection : PropertyDescriptorCollection
{
public DesignerPropertyDescriptorCollection(PropertyDescriptor[] properties)
: base(properties, readOnly: true)
{
}
public override PropertyDescriptor Find(string name, bool ignoreCase)
{
// Guaranteed to return a descriptor for any property that is being looked up, whether it has any meaning or not.
return base.Find(name, ignoreCase) ?? new DummyPropertyDescriptor(name);
}
}
// A property descriptor that has no state whatsoever.
class DummyPropertyDescriptor : PropertyDescriptor
{
public DummyPropertyDescriptor(string name)
: base(name, new Attribute[0])
{
}
public override bool CanResetValue(object component)
{
return false;
}
public override Type ComponentType
{
get { return typeof(object); }
}
public override object GetValue(object component)
{
return null;
}
public override bool IsReadOnly
{
get { return false; }
}
public override Type PropertyType
{
get { return typeof(object); }
}
public override void ResetValue(object component)
{
}
public override void SetValue(object component, object value)
{
}
public override bool ShouldSerializeValue(object component)
{
return false;
}
}
}