Использование BindingSource в UserControl - PullRequest
10 голосов
/ 11 июня 2009

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

Ответы [ 4 ]

5 голосов
/ 22 сентября 2009

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

Во-первых, давайте рассмотрим следующий 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, то вам нужно будет написать метод, который мог бы определять тип объекта. объект прошел, затем связал свойства соответственно. Хороший способ, которым вы могли бы пойти, возможно, с помощью Reflection, если вам удобно работать с ним. Любым возможным способом, который вы можете себе представить, работая с такими функциями полиморфизма, вам придется написать себе интерфейс, который должны реализовать все ваши привязываемые объекты. Таким образом, вы избежите неизвестных имен свойств, и когда придет время привязать элементы управления вашего 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.

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

1 голос
/ 16 ноября 2014

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

У меня есть элементы управления на 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 , Когда 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;
    }
}
1 голос
/ 03 ноября 2011

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

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

Меньше набирается текста и меньше опечаток, потому что дизайнер VS создает все эти литеральные имена свойств текста.

1 голос
/ 23 сентября 2009

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

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
...