Использование BindingSource в UserControl

У меня есть UserControl с несколькими полями, которые я хотел бы связать с BindingSource. Я также хотел бы, чтобы UserControl обнаружил некоторое свойство BindingSource, чтобы его можно было отбросить на форму и привязать к BindingSource в форме. Есть простой способ сделать это? Я понимаю, что я могу перепроверять все элементы управления UserControl в своем наборе BindSource. Но это кажется неправильным. Есть ли какой-то прокси-сервер BindingSource, который позволит мне связать BindingSource в пользовательском элементе управления с BindingSource в форме?

Ответ 1

В соответствии с вашим вопросом я вряд ли получу то, что вы намереваетесь сделать. Поэтому я постараюсь изо всех сил предоставить вам, надеюсь, интересную информацию по этому вопросу.

Во-первых, рассмотрим следующий UserControl в проекте программного обеспечения для управления клиентами.

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

Во-вторых, рассмотрим следующую форму, которая должна быть вашей формой управления клиентом.

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;
}

Если вы ожидаете использовать свойство CustomerManagementUserControl.DataSource из окна свойств, рассмотрите возможность добавления следующего в дополнение к определению вашего свойства.

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

Это один из способов сделать то, что, я думаю, вы захотите сделать. С другой стороны, если вы хотите сделать как можно более абстрактное, установив в качестве свойства UserControl.BindingSource.DataSource объект другого типа, тогда вам нужно будет написать метод, который мог бы определить тип объект прошел, а затем привязал свойства соответственно. Возможно, хороший способ, которым вы могли бы пойти, - это отразить, если вам удобно работать с ним. В любом возможном случае вы можете представить себе работу с такими функциями полиморфизма, вам придется написать себе интерфейс, который будет реализован всеми вашими связываемыми объектами. Таким образом, вы избежите неизвестных имен свойств, и когда придет время связывать элементы управления UserControl, вы сможете привязать правильное свойство к правильному управлению и т.д.

Попробуйте следующее:

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; }
}

Учитывая приведенный выше код, вы можете использовать свойство DataSource вашего UserControl для привязки к IEntity, поэтому вашему имуществу может понравиться подобное.

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

Тем не менее, если вы хотите продвинуться еще дальше, вы можете просто подвергнуть свои свойства DataBindings вашим элементам управления UserControl, чтобы установить их во время разработки. Учитывая это, вы захотите открыть свой BindingSource как общедоступное свойство, чтобы вы могли установить его также во время разработки, а затем выберите свой DataMember из этого BindinSource.

Надеюсь, это поможет вам немного или, по крайней мере, дать вам несколько треков для дальнейших поисков.

Ответ 2

Если вы хотите сделать это все автоматически, вы можете искать источник привязки из родительской формы в событии загрузки вашего пользовательского элемента управления или что-то в этом роде...

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

Ответ 3

Если вы назначаете ту же ссылку на объект, что и источник данных, на два источника привязки, элементы управления не будут последовательно обновляться во втором источнике привязки. Возможно, компромисс с вышеперечисленными вариантами заключается в следующем:

  • Временно добавьте источник привязки к usercontrol и используйте конструктор VS, чтобы установить привязки к элементам управления.
  • верните designer.vb в редактор кода. Найдите все строки "DataBindings.Add", созданные дизайнером. Скопируйте их все в блокнот.
  • удалите источник привязки от конструктора и добавьте ссылку привязки в код. Добавьте свойство для источника связывания с тем же именем, которое использовалось в конструкторе. В установщике для свойства вставьте все строки из блокнота выше на шаге 2.
  • В событии Load формы присвойте bindingsource формы свойству в пользовательском элементе управления. Если пользовательский элемент управления встроен в другой пользовательский элемент управления, вы можете использовать событие handlecreated родительского элемента управления, чтобы сделать то же самое.

Существует меньше типизации и меньше опечаток, потому что конструктор VS создает все эти имена свойств литерала.

Ответ 4

Я знаю это поздний ответ; тем не менее, может быть полезно кому-то еще прочитать это сообщение.

У меня есть элементы управления UserControl, привязанные к данным. Мне нужно иметь BindingSource на UserControl, чтобы иметь возможность связывать элементы управления во время разработки. Однако "реальный" BindingSource находится на Form. Другими словами, элементы управления UserControl должны вести себя так, как если бы они сидели непосредственно на форме (или на ContainerControl в форме).

Идея этого решения состоит в том, чтобы наблюдать за событием DataSourceChanged "реального" BindingSource и назначать его DataSource локальному BindingSource при его изменении. Чтобы найти "реальный" BindingSource, я позволяю Form (или Control), содержащему его, реализовать следующий интерфейс:

public interface IDataBound
{
    BindingSource BindingSource { get; }
}

Мы можем наблюдать за событием ParentChanged элемента управления, чтобы узнать, когда он был добавлен в Form или ContainerControl. Проблема здесь в том, что этот ContainerControl сам, возможно, еще не был добавлен в Form (или другой ContainerControl) еще в это время. В этом случае мы подписываемся на событие ParentChanged последнего родителя, которое мы находим в цепочке родителей, и ожидаем, пока этот последний родитель не будет добавлен, так далее, пока мы не найдем реализацию Control или Form IDataBound, Когда a IDataBound найдено, мы подписываемся на событие DataSourceChanged его 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;
    }
}