Using a BindingSource in a UserControl
Asked Answered
T

5

11

I have a UserControl with multiple fields that I would like to have bound to a BindingSource. I would also like the UserControl to expose some BindingSource property so that it can be dropped on a Form and be bound to the BindingSource on the form. Is there an easy way to do this? I realize that I can rebind all of the controls of the UserControl in its BindSource setter. But this seems wrong. Is there some BindingSource Proxy that will let me link the BindingSource in the user control to the BindingSource in the form?

Transvalue answered 11/6, 2009 at 17:9 Comment(4)
You might perhaps expose your BindingSource.DataSource object property by writing a property you would call DataSource, and this would set your form's BindingSource.DataSource property. Otherwise, please try to explain further what you wish to do or give a concrete example that could help us understand better. It's still a bit fuzzy in my head when I read it. Do you want to set the DataSource on design-time? Do you want to expose BindingSource properties in the Property window? Do you want to bind your UserControl controls to a specific DataMember from your BindingSource 'n change on design?Floe
Perhaps could you accept the answer the others provide to your question. I think this would be a nice way to thank those who took this time to answer and help you, whether they helped or not, they took this time for you.Floe
Please use full names when you refer to objects and properties. Be specific, give some code or simple examples. Do not expect from us to understand a missing context.Hulen
There is a DataRepeater in Visual Basic Power Pack which works fine and handle the tedious job of creation/deletion/binding of the detail controls msdn.microsoft.com/en-us/library/cc425010(v=vs.80).aspxGauguin
F
5

As per your question, I can hardly get what you intend to do. Thus I will try my best to provide you with, I hope, interesting information on that matter.

First, let's consider the following UserControl in a Customer management software project.

public partial class CustomerManagementUserControl : UserControl {
    public CustomerManagementUserControl() {
        InitializeComponent();
        _customerBindingSource = new BindingSource();
    }
    public IList<ICustomer> DataSource {
        set {
            _customerBindingSource.DataSource = value;
        }
    }
    private BindingSource _customerBindingSource;
}

Second, let's consider the following Form which should be your Customer management form.

public partial class CustomerManagementForm : Form {
    public CustomerManagementForm() {
        InitializeComponent();
        _customerUserControl = new CustomerManagementUserControl();
        _customerUserControl.Name = @"customerUserControl";
    }
    private void CustomerManagementForm_Load(object sender, EventArgs e) {
        // CustomersFacade is simply a static class providing customer management features and requirements.
        // Indeed, the GetCustomers() method shall return an IList<ICustomer>.
        // The IList type and typed IList<T> are both intended to be bindable as a DataSource for DataBinding.
        _customerUserControl.DataSource = CustomersFacade.GetCustomers();
        this.Controls.Add(_customerUserControl);
    }
    private CustomerManagementUserControl _customerUserControl;
}

If you're expecting to use CustomerManagementUserControl.DataSource property from within the Property window, please consider adding the following on top of your property definition.

[System.ComponentModel.DesignTimeVisible(true), System.ComponentModel.DesignerCategory("CustomerUserControl"), System.ComponentModel.Description("Sets the CustomerUserControl DataSource property")]

This is one way of doing what I guess you might want to do. On the other hand, if what you wish to do is to get the as most abstract as possible by setting a different type of object as your UserControl.BindingSource.DataSource property, then you will have to write a method which could detect the type of the object passed, then binding the properties accordingly. A nice way you could go, perhaps, is by Reflection, if you're comfortable working with it. In any possible way you may imagine working with such polymorphism features, you will have to write yourself an interface that all of your bindable objects will have to implement. This way, you will avoid unknown property names, and when will come the time to bind your UserControl's controls, you will be able to bind the correct property to the correct control and so forth.

Let's try the following:

public interface IEntity {
    double Id { get; set; }
    string Number { get; set; }
    string Firstname { get; set; }
    string Surname { get; set; }
    long PhoneNumber { get; set; }
}
public interface ICustomer : IEntity {
}
public interface ISupplier : IEntity {
    string Term { get; set; }
}
public sealed Customer : ICustomer {
    public Customer() {
    }
    public double Id { get; set; }
    public string Number { get; set; }
    public string Firstname { get; set; }
    public string Surname { get; set; }
    public long PhoneNumber { get; set; }    
}
public sealed Supplier : ISupplier {
    public Supplier() {
    }
    public double Id { get; set; }
    public string Number { get; set; }
    public string Firstname { get; set; }
    public string Surname { get; set; }
    public long PhoneNumber { get; set; }    
    public string Term { get; set; }
}

Considering the above code, you could use the DataSource property of your UserControl to bind with an IEntity, so your property could like like this.

[System.ComponentModel.DesignTimeVisible(true), System.ComponentModel.DesignerCategory("CustomerUserControl"), System.ComponentModel.Description("Sets the CustomerUserControl DataSource property")]
public IList<IEntity> DataSource {
    set {
        _customerBindingSource.DataSource = value;
    }
}

That said, if you wish to push even further, you could just expose your UserControl's controls DataBindings properties in order to set them on design-time. Considering this, you will want to expose your BindingSource as a public property either so that you may set it on design-time too, then choose your DataMember from this BindinSource.

I hope this helps you both a little or at least, give you some tracks for further searchings.

Floe answered 22/9, 2009 at 14:11 Comment(0)
M
2

I know it's a late answer; however, it might be useful to someone else reading this post.

I have controls on a UserControl that are data-bound. I need to have a BindingSource on the UserControl in order to be able to bind the controls at design time. The "real" BindingSource, however, sits on the Form. In other words, the controls on the UserControl should behave as if they were sitting directly on the form (or on a ContainerControl on the form).

The idea behind this solution is to watch for the DataSourceChanged event of the "real" BindingSource and to assign its DataSource to the local BindingSource when it changes. In order to find the "real" BindingSource I let the Form (or Control) containing it implement the following interface:

public interface IDataBound
{
    BindingSource BindingSource { get; }
}

We can watch for the ParentChanged event of a control in order to know when it has been added to a Form or a ContainerControl. The problem here is that this ContainerControl itself might not have been added to the Form (or another ContainerControl) yet at this time. In this case we subscribe to the ParentChanged event of the last parent we find in the parents chain and wait until this last parent has been added, an so on, until we find a Control or Form implementing IDataBound. When a IDataBound has been found, we subscribe to the DataSourceChanged event of its BindingSource.

public partial class MyUserControl : UserControl
{
    private IDataBound _dataBoundControl;
    private Control _parent;

    public MyUserControl()
    {
        InitializeComponent();
        if (LicenseManager.UsageMode == LicenseUsageMode.Runtime) {
            _parent = this;
            SearchBindingSource();
        }
    }

    private void SearchBindingSource()
    {
        if (_parent != null && _dataBoundControl == null) {
            while (_parent.Parent != null) {
                _parent = _parent.Parent;
                _dataBoundControl = _parent as IDataBound;
                if (_dataBoundControl != null) {
                    if (_dataBoundControl.BindingSource != null) {
                        _dataBoundControl.BindingSource.DataSourceChanged +=
                            new EventHandler(DataBoundControl_DataSourceChanged);
                    }
                    return;
                }
            }
            // This control or one of its parents has not yet been added to a
            // container. Watch for its ParentChanged event.
            _parent.ParentChanged += new EventHandler(Parent_ParentChanged);
        }
    }

    void Parent_ParentChanged(object sender, EventArgs e)
    {
        SearchBindingSource();
    }

    void DataBoundControl_DataSourceChanged(object sender, EventArgs e)
    {
        localBindingSource.DataSource = _dataBoundControl.BindingSource.DataSource;
    }
}
Matty answered 15/11, 2014 at 21:49 Comment(0)
F
1

If you wanted to do this all automatically you could look for the binding source from the parent form in the load event of your user control or something like that...

Dim components As Reflection.FieldInfo = typ.GetField("components", Reflection.BindingFlags.DeclaredOnly Or Reflection.BindingFlags.Instance Or Reflection.BindingFlags.NonPublic)

Dim lstBindingSources As New List(Of BindingSource)
For Each obj As Object In components.Components
   Dim bindSource As BindingSource = TryCast(obj, BindingSource)
   If bindSource IsNot Nothing Then
      lstBindingSources.Add(bindSource)
   End If
Next
If lstBindingSources.Count = 1 Then
   MyBindingSource.DataSource = lstBindingSources(0).DataSource
End If
Frig answered 23/9, 2009 at 10:59 Comment(0)
O
1

If you assign the same object reference as the datasource on two bindingsources, the controls will not be updated consistently on the second bindingsource. Possibly, a compromise to the choices above is the following:

  1. Temporarily add a bindingsource to the usercontrol and use the VS designer to set the bindings to the controls.
  2. bring the designer.vb up in the code editor. Search for all the "DataBindings.Add" lines that were created by the designer. Copy them all to notepad.
  3. delete the bindingsource from the designer and add a bindingsource reference in code. Add a property for the bindingsource with the same name as was used in the designer. In the setter for the property, paste all the lines from notepad above in step 2.
  4. In the Load event of the form, assign the bindingsource of the form to the property on the user control. If the user control is embedded in another user control, you can use the handlecreated event of the parent control to do the same.

There is less typing and less typos because the VS designer is creating all those literal text property names.

Orchestral answered 3/11, 2011 at 13:49 Comment(0)
R
0

This discussion was useful for discovering alternate methods for binding a login model to a custom UserControl for password entry on a WinForms window. Of particular interest was @Olivier's post implementing an IDataBound interface. While probably overkill for the simplicity of a login model, I wanted to test the validity. Exposing a property to return the password from the custom password_box, would be simple and straightforward. What I don't know is how easy it may be to exploit that property. As @WillMarcouiller commented, It is possible to expose a BindingSource control on the password_box.

In the end, the result was to include the step to bind the TextBox with the BindingSource as @DavidWilks suggested, only in the event handler for the Binding DataSource changing.

void DataBoundControl_DataSourceChanged(object sender, EventArgs e)
{
    localBindingSource.DataSource = _dataBoundControl.BindingSource.DataSource;
    textBox1.DataBindings.Add(new Binding("Text", localBindingSource, "Password", false));
}

What this example doesn't address is multiple instances of a UserControl on a form. For now, I'm thinking that the easiest solution would be to create a property in the control to have the source name assigned to which could be handled in the designer. At least in VS2022, there is a miscellaneous section that takes in properties exposed by the UserControl.

Rounders answered 25/5 at 20:32 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.