我有一个包含多个字段的 UserControl,我想将这些字段绑定到 BindingSource。我还希望 UserControl 公开一些 BindingSource 属性,以便可以将其拖放到表单上并绑定到表单上的 BindingSource。是否有捷径可寻?我意识到我可以在其 BindSource 设置器中重新绑定 UserControl 的所有控件。但这似乎是错误的。是否有一些 BindingSource 代理可以让我将用户控件中的 BindingSource 链接到表单中的 BindingSource?
4 回答
根据你的问题,我很难得到你打算做什么。因此,我将尽力为您提供有关此事的有趣信息。
首先,让我们考虑一个客户管理软件项目中的以下 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; }
}
考虑到上面的代码,您可以使用 UserControl 的 DataSource 属性与 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;
}
}
也就是说,如果您希望更进一步,您可以只公开您的 UserControl 控件的 DataBindings 属性,以便在设计时设置它们。考虑到这一点,您将希望将 BindingSource 公开为公共属性,以便您也可以在设计时设置它,然后从此 BindinSource 中选择您的 DataMember。
我希望这对你们都有一点帮助,或者至少能给你们一些进一步搜索的线索。
我知道这是一个迟到的答案;但是,它可能对阅读这篇文章的其他人有用。
我有一个UserControl
数据绑定的控件。我需要一个BindingSource
onUserControl
以便能够在设计时绑定控件。然而, “真实”BindingSource
位于Form
. 换句话说, 上的控件UserControl
应该表现得好像它们直接位于表单上(或在ContainerControl
表单上)。
这个解决方案背后的想法是观察DataSourceChanged
“真实”的事件,BindingSource
并在它发生变化时将其分配DataSource
给本地BindingSource
。为了找到“真实” BindingSource
,我让包含它的Form
(或Control
)实现以下接口:
public interface IDataBound
{
BindingSource BindingSource { get; }
}
我们可以观察ParentChanged
控件的事件,以便知道它何时被添加到 aForm
或 aContainerControl
中。这里的问题是,这ContainerControl
本身可能还没有被添加到Form
(或另一个ContainerControl
)中。在这种情况下,我们订阅了在ParentChanged
父链中找到的最后一个父事件,并等到最后一个父被添加,依此类推,直到我们找到一个Control
或Form
实现IDataBound
。当 aIDataBound
被找到时,我们订阅它的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;
}
}
如果您将相同的对象引用指定为两个绑定源上的数据源,则控件将不会在第二个绑定源上一致更新。可能,对上述选择的妥协如下:
- 暂时将绑定源添加到用户控件并使用
VS
设计器将绑定设置到控件。 designer.vb
在代码编辑器中调出。搜索"DataBindings.Add"
设计师创建的所有线条。将它们全部复制到记事本。- 从设计器中删除 bindingsource 并在代码中添加 bindingsource 引用。为绑定源添加一个与设计器中使用的名称相同的属性。在属性的设置器中,粘贴上面步骤 2 中记事本中的所有行。
- 在窗体的 Load 事件中,将窗体的 bindingsource 分配给用户控件上的属性。如果用户控件嵌入在另一个用户控件中,则可以使用父控件的 handlecreated 事件来执行相同操作。
VS
因为设计师正在创建所有这些文字文本属性名称,所以打字和错别字更少。
如果您想自动完成这一切,您可以在用户控件的加载事件或类似的事情中从父表单中查找绑定源......
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