Pergunta

Sometimes it is helpful to inherit from a base form or base user control that supplies a binding source to the inheritors.

The inheriting classes can for instance set the data source to their specific type at design time or bind controls- all without writing special code in each inherited control to manage the binding source. You can even use master-detail by adding child binding sources with the data member property set.

Child binding works perfectly at runtime, but unfortunately, the designer trips on this at design time when opening a saved child control. Instead of actually executing the code in InitializeComponent, the designer uses deserializers to interpret the text. In this case, the base class's binding source hasn't been set up yet when the child class tries to find the data member on it.

Long story short: perfect runtime code, but design time error "Data member not found on the data source."

Foi útil?

Solução

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;
        }
    }
}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top