Multiple BindingSource components necessary for just one data source?

1.5k views Asked by At

As per my previous question, Make properties available for data binding through some kind of interface in .NET?, I managed, with the help of @Marc Gravell that by implementing the interface ICustomTypeDescriptor I can provide the form designer with custom properties that may or may not actually be visible on the component in question as real normal properties.

I managed this, but my implementation is flawed. The biggest problem I have now is that if I drop a component on my form, which has two such custom properties, and then drop two textboxes onto the form, and use data binding, the data binding dropdown in the property inspector needs an object data source, but each property I bind to a control adds another BindingSource component onto the form.

Let me rephrase that. I drop my custom component onto the form. It has two properties, that are available through my ICustomTypeDescriptor implementation, but not visible as normal properties.

I then drop two textboxes onto the form. I go to the property inspector for one of them to add data binding for the Text property, and it needs an object data source in the project, and I add that. Then, after binding the Text property on the textbox to the first property of my custom component, the form designer has another component added to it, a "customDataBindingBindingSource", which is used to bridge the data binding between the two. So far, so good.

Then, when I set up data binding for that other textbox in the same way (except that I can now just pick the other property of my custom component), another such bridge is added, "customDataBindingBindingSource1". If I keep switching properties to bind to, one such bridge is added each time.

Is this really necessary?

If not, what did I do wrong in my ICustomTypeDescriptor implementation? Granted, it's naive and simple and far from complete, but I don't know what I need to fix. Any pointers?

Here's my custom class:

public class CustomDataBinding : Component, ICustomTypeDescriptor, INotifyPropertyChanged
{
    private String _Property1;
    private String _Property2;

    public class MyPropertyDescriptor : PropertyDescriptor
    {
        private String _Name;

        public MyPropertyDescriptor(String name)
            : base(name, null)
        {
            _Name = name;
        }

        public override bool CanResetValue(object component)
        {
            return true;
        }

        public override Type ComponentType
        {
            get { return typeof(CustomDataBinding); }
        }

        public override object GetValue(object component)
        {
            CustomDataBinding source = (CustomDataBinding)component;
            switch (_Name)
            {
                case "Property1":
                    return source._Property1;
                    break;

                case "Property2":
                    return source._Property2;
                    break;

                default:
                    return null;
            }
        }

        public override bool IsReadOnly
        {
            get { return false; }
        }

        public override Type PropertyType
        {
            get { return typeof(String); }
        }

        public override void ResetValue(object component)
        {
            SetValue(component, _Name);
        }

        public override void SetValue(object component, object value)
        {
            CustomDataBinding source = (CustomDataBinding)component;
            switch (_Name)
            {
                case "Property1":
                    source._Property1 = Convert.ToString(value);
                    Debug.WriteLine("Property1 changed to " + value);
                    break;

                case "Property2":
                    source._Property2 = Convert.ToString(value);
                    Debug.WriteLine("Property2 changed to " + value);
                    break;

                default:
                    return;
            }
            source.OnPropertyChanged(_Name);
        }

        public override bool ShouldSerializeValue(object component)
        {
            return false;
        }
    }

    public CustomDataBinding()
    {
        _Property1 = "Property1";
        _Property2 = "Property2";
    }

    #region ICustomTypeDescriptor Members

    public AttributeCollection GetAttributes()
    {
        return new AttributeCollection(null);
    }

    public string GetClassName()
    {
        return null;
    }

    public string GetComponentName()
    {
        return null;
    }

    public TypeConverter GetConverter()
    {
        return null;
    }

    public EventDescriptor GetDefaultEvent()
    {
        return null;
    }

    public PropertyDescriptor GetDefaultProperty()
    {
        return null;
    }

    public object GetEditor(Type editorBaseType)
    {
        return null;
    }

    public EventDescriptorCollection GetEvents(Attribute[] attributes)
    {
        return new EventDescriptorCollection(null);
    }

    public EventDescriptorCollection GetEvents()
    {
        return new EventDescriptorCollection(null);
    }

    public PropertyDescriptorCollection GetProperties(Attribute[] attributes)
    {
        return new PropertyDescriptorCollection(new PropertyDescriptor[] {
            new MyPropertyDescriptor("Property1"),
            new MyPropertyDescriptor("Property2") });
    }

    public PropertyDescriptorCollection GetProperties()
    {
        return GetProperties(null);
    }

    public object GetPropertyOwner(PropertyDescriptor pd)
    {
        return this;
    }

    #endregion

    #region INotifyPropertyChanged Members

    public event PropertyChangedEventHandler PropertyChanged;

    public void OnPropertyChanged(String name)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(name));
    }

    public void SetValues(String p1, String p2)
    {
        _Property1 = p1;
        _Property2 = p2;

        OnPropertyChanged("Property1");
        OnPropertyChanged("Property2");
    }

    #endregion
}

Also, I need to manually hook these bridges up to the component I originally, dropped, in the form constructor, like this:

customDataBindingBindingSource.DataSource = customDataBinding1;
customDataBindingBindingSource1.DataSource = customDataBinding1;

Is there a way to get around a lot of this?

Basically, what I'd like to do when using my components is this:

  1. Drop one of my custom components onto the form
  2. Drop the necessary controls (text boxes, date pickers, etc.)
  3. Assign data binding for the controls to the relevant properties on my custom component

If possible, I'd like to avoid having the project data source, and the bridge components, as well as the code in the constructor. All that I'd like to avoid.

Note that I do not ask for anyone to give me teh codes. Any pointers are welcome.

0

There are 0 answers