在 UserControl 中使用 BindingSource

发布于 2024-07-25 04:51:37 字数 276 浏览 6 评论 0原文

我有一个包含多个字段的 UserControl,我希望将其绑定到 BindingSource。 我还希望 UserControl 公开一些 BindingSource 属性,以便可以将其放在表单上并绑定到表单上的 BindingSource。 是否有捷径可寻? 我意识到我可以在其 BindSource setter 中重新绑定 UserControl 的所有控件。 但这似乎是错误的。 是否有一些 BindingSource 代理可以让我将用户控件中的 BindingSource 链接到表单中的 BindingSource?

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

谢绝鈎搭 2024-08-01 04:51:37

根据你的问题,我很难理解你想要做什么。 因此,我希望我会尽力为您提供有关此事的有趣信息。

首先,让我们考虑客户管理软件项目中的以下 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。

我希望这对您有一点帮助,或者至少为您提供一些进一步搜索的线索。

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.

念﹏祤嫣 2024-08-01 04:51:37

我知道这是一个迟到的答复; 但是,对于阅读这篇文章的其他人来说可能会有用。

我在 UserControl 上有数据绑定的控件。 我需要在 UserControl 上有一个 BindingSource ,以便能够在设计时绑定控件。 然而,“真正的”BindingSource 位于Form 上。 换句话说,UserControl 上的控件的行为应该就像它们直接位于表单上(或位于表单上的 ContainerControl 上)一样。

此解决方案背后的想法是监视“真实”BindingSourceDataSourceChanged 事件,并将其 DataSource 分配给本地 BindingSource 当它改变时。 为了找到“真正的”BindingSource,我让包含它的 Form(或 Control)实现以下接口:

public interface IDataBound
{
    BindingSource BindingSource { get; }
}

我们可以观察控件的 ParentChanged 事件,以便了解何时将其添加到 FormContainerControl。 这里的问题是,此 ContainerControl 本身此时可能尚未添加到 Form(或另一个 ContainerControl)中。 在本例中,我们订阅在父级链中找到的最后一个父级的 ParentChanged 事件,并等待添加最后一个父级,依此类推,直到找到 Control code> 或 Form 实现 IDataBound。 当找到 IDataBound 时,我们订阅其 BindingSourceDataSourceChanged 事件。

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

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;
    }
}
苍暮颜 2024-08-01 04:51:37

如果您想自动完成这一切,您可以在用户控件的加载事件或类似的事件中从父表单中查找绑定源...

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

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
咽泪装欢 2024-08-01 04:51:37

如果您在两个绑定源上分配与数据源相同的对象引用,则控件将不会在第二个绑定源上一致更新。 可能,对上述选择的折衷方案如下:

  1. 暂时向用户控件添加绑定源,并使用 VS 设计器设置控件的绑定。
  2. 在代码编辑器中调出designer.vb。 搜索设计器创建的所有“DataBindings.Add”行。 将它们全部复制到记事本中。
  3. 从设计器中删除绑定源并在代码中添加绑定源引用。 为绑定源添加一个属性,其名称与设计器中使用的名称相同。 在属性的 setter 中,粘贴上面步骤 2 中记事本中的所有行。
  4. 在窗体的 Load 事件中,将窗体的绑定源分配给用户控件上的属性。 如果用户控件嵌入到另一个用户控件中,则可以使用父控件的handlecreated事件来执行相同的操作。

由于 VS 设计器正在创建所有这些文字文本属性名称,因此可以减少打字次数和拼写错误。

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.

江南烟雨〆相思醉 2024-08-01 04:51:37

此讨论对于发现将登录模型绑定到自定义 UserControl 以在 WinForms 窗口上输入密码的替代方法很有用。 特别有趣的是@Olivier 的帖子实现了 IDataBound 接口。 虽然登录模型的简单性可能有点过分,但我想测试其有效性。 公开一个属性以从自定义密码框返回密码将非常简单明了。 我不知道利用这一财产有多么容易。 正如@WillMarcouiller 评论的那样,可以在password_box 上公开BindingSource 控件。

最后,结果是按照 @DavidWilks 的建议,仅在绑定数据源更改的事件处理程序中包含将 TextBox 与 BindingSource 绑定的步骤。

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

此示例没有解决表单上 UserControl 的多个实例。 目前,我认为最简单的解决方案是在控件中创建一个属性,以将源名称分配给可以在设计器中处理的源名称。 至少在 VS2022 中,有一个杂项部分接收 UserControl 公开的属性。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文