【问题标题】:Binding control to Object vs DataRow将控件绑定到 Object vs DataRow
【发布时间】:2019-01-18 10:43:37
【问题描述】:

我写了一些示例代码来演示我的问题。一个绑定到 Object,另一个绑定到 DataRow:

绑定到 DataRow 示例:

namespace WindowsFormsApplication1
{
    public partial class frmBindExample : Form
    {
        public frmBindExample()
        {
            InitializeComponent();
            InitForm();
        }



        private void InitForm()
        {
            //;; Init the list
            DataTable dt = new DataTable();
            dt.Columns.Add(new DataColumn("Id"));
            dt.Columns.Add(new DataColumn("Name"));
            dt.Rows.Add(new string[] { "5476", "Smith" });
            dt.Rows.Add(new string[] { "5477", "Marlin" });


            Label label1 = new Label() { Top = 130, Left = 10, Text = "Id of Smith is:" };
            this.Controls.Add(label1);

            //;; Bind two direction with TextBox. 
            TextBox textbox1 = new TextBox() { Top = 130, Left = 130, Width = 100 };
            this.Controls.Add(textbox1);
            textbox1.DataBindings.Add("Text", dt.Rows[0], "Id");


            //;; The binding system respose changing property value
            Button button1 = new Button() { Top = 160, Left = 10, Width = 200, Text = "Set Id=99 Directly by property" };
            this.Controls.Add(button1);
            button1.Click += (s, e) =>
            {
                dt.Rows[0]["Id"] = "99";
            };

            DataGridView dg = new DataGridView() { Top = 200, Left = 10 };
            this.Controls.Add(dg);
            dg.DataSource = dt;
        }


    }
}

看起来像:

如您所见,与 TextBox 的绑定在下一个示例中不起作用。但是,当我按下按钮更新字段时,数据网格会立即刷新:

好的,现在看看如果我绑定 Object 是什么 hempen:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace WindowsFormsApplication1
{
    public partial class frmBindExample : Form
    {
        public frmBindExample()
        {
            InitializeComponent();
            InitForm();
        }


        private void InitForm()
        {
            //;; Init the list
            List<Person> lst = new List<Person>();
            lst.Add(new Person() { Id = "5476", Name = "Smith" });
            lst.Add(new Person() { Id = "5477", Name = "Marlin" });


            Label label1 = new Label() { Top = 130, Left = 10, Text = "Id of Smith is:" };
            this.Controls.Add(label1);

            //;; Bind two direction with TextBox. 
            TextBox textbox1 = new TextBox() { Top = 130, Left = 130, Width = 100 };
            this.Controls.Add(textbox1);
            textbox1.DataBindings.Add("Text", lst[0], "Id");

            //;; The binding system respose changing property value
            Button button1 = new Button() { Top = 160, Left = 10, Width = 200, Text = "Set Id=99 Directly by property" };
            this.Controls.Add(button1);
            button1.Click += (s, e) =>
            {
                lst[0].Id = "99";
            };

            DataGridView dg = new DataGridView() { Top = 200, Left = 10 };
            this.Controls.Add(dg);
            dg.DataSource = lst;
        }

    }


    //;; The person class can bind any his property without any extra call fo change detection 
    public class Person
    {
        public string Id { get; set;}
        public string Name { get; set; }
    }
}

现在,TextBox 显示我们的 Id 值。但按下设置按钮不会刷新 DataGrid 上的数据。

所以,我的问题是:

  1. 为什么绑定 TextBox 在第一个示例中不能正常工作?
  2. 确实可以说,自动(无需任何额外的 do 绑定调用)将更新从源传播到控件只发生在 DataRow 上?

【问题讨论】:

  • 使用BindingList&lt;Person&gt; 而不是List&lt;T&gt;。另外,使用textbox1.DataBindings.Add("Text", lst, "Id");
  • 我尝试:BindingList lst = new BindingList();,但这并没有改变结果。 DatagridView 仍然没有像 DataRow 版本那样刷新。
  • 我没有看到代码。您可以做几件事:1) 为TextBox.TextChanged 事件添加一个事件处理程序。在处理程序方法中,调用this.dg.UpdateCellValue(0, this.dg.CurrentRow.Index); 2) 创建一个BindingSource 并将其DataSource 设置为您的列表。在向TextBox添加Binding时,订阅Binding的Parse事件,那里调用[YourBindingSource].CurrencyManager.Refresh();。还有更多可用的方法。
  • 是的。你说得对,有额外的代码我可以做到。但我发现 DataSource、Datatable 和 BindingList 类型与对象属性之间存在很大差异。第一个无需任何额外代码即可刷新 UI。请问这个结论是否正确,与对象属性的绑定必须额外的代码才能刷新。
  • 不使用 BindingSource。如果将BindingSource.DataSource 设置为BindingList,然后使用BindingSource 作为数据源将绑定添加到TextBox 属性,同时使用BindingSource 作为DataGridView.DataSource,更新将是自动的。我建议订阅 Binding 的 Parse 事件,因为您可能希望在提交新值之前验证输入的值。如果您需要示例,请告诉我。

标签: c# .net winforms data-binding


【解决方案1】:

为什么绑定 TextBox 在第一个示例中无法正常工作?

这是因为DataRowTypeDescriptor 没有Id 属性。考虑以下规则:

  • 当数据绑定到项目的属性时,项目的类型描述符应包含具有该名称的属性。

  • 当数据绑定到列表时,列表项类型描述符应包含具有该名称的属性。

是不是说,自动(无需任何额外的 do 绑定调用)将更新从源传播到控件只发生在 DataRow 上?

没有。这不是因为DataRow 类型。这是因为INotifyPropertyChangedIBindingList。考虑以下规则:

  • 当数据绑定控件到item时,如果item实现了INotifyPropertyChanged,那么在item更新后UI会立即更新。

  • 数据绑定到列表控件时,如果item实现INotifyPropertyChanged,list实现IBindingList,则更新item或list后UI会立即更新。

更多信息

我上面所说的很简短,你可以在Windows Forms Data Binding中找到详细信息。我建议阅读以下有用的文档:

【讨论】:

  • 帖子直接回答你的问题。如果您对答案有任何疑问,请告诉我。
【解决方案2】:

因为,也许,在 cmets 中描述这个过程不是一个好主意,这里是 放大的 版本。

  • BindingListList&lt;T&gt; 定义为数据存储对象(我更喜欢BindingList,但在这里,一个简单的List 无论如何都可以完成这项工作)。

  • 定义一个BindingSource,它将提供更改通知和货币管理。它大大简化了WinForms中控件的绑定(类对象应该实现INotifyProeprtyChanged,但是对于这个例子来说并不重要。在更具体的场景中,当你有双向绑定需要更新时,它可能变得很重要UI 和数据源)。

  • BindingSource.DataSource 属性设置为提供数据的对象:此处为BindingListIList

  • Binding 添加到TextBox.Text 属性,该属性将绑定到数据源的属性(或DataTable 的列),设置DataSourceBindingBindingSourceDataMember 值到 TextBox 属性绑定到的数据源的属性(或列)。这里是 Input 类的 Id 属性。

  • 订阅 TextBox BindingParse event,以便在允许更新数据源之前验证输入到 TextBox 中的数据。如果输入的值不符合描述(即用户输入的是字母而不是数字),我们可以调用例如BindingSource.ResetCurrentItem方法取消数据更新

  • DataGridView.DataSource 设置为 BindingSource

这就是发生的事情,使用此处显示的代码:


注意
我在这里使用 lambda 订阅 Parse 事件;如果您需要多次订阅/取消订阅此事件,则可能需要使用单独的处理程序。

internal class Input
{
    public int Id { get; set; }
    public string Name { get; set; }
}

internal List<Input> inputData = new List<Input>();
internal BindingSource bindingSource;

private void button1_Click(object sender, EventArgs e)
{
    bindingSource = new BindingSource();

    inputData.AddRange(new [] { 
        new Input() { Id = 5476, Name = "Smith" },
        new Input() { Id = 5477, Name = "Marlin" }
    });

    bindingSource.DataSource = inputData;

    Binding tboxBind = new Binding("Text", bindingSource, "Id", false, DataSourceUpdateMode.OnPropertyChanged);

    tboxBind.Parse += (pObj, pEvt) =>
    {
        if (!int.TryParse(pEvt.Value.ToString(), out int value))
            bindingSource.ResetCurrentItem();
    };
    textBox1.DataBindings.Add(tboxBind);
    dataGridView1.DataSource = bindingSource;
}

【讨论】:

  • 谢谢。但我询问数据属性的更改值。比如:private void button2_Click(object sender, EventArgs e){InputData[0].Id = 99;}。在 DtatRow 上它会立即刷新,但不会在 List 上。
  • 是的,它会立即刷新。测试我发布的代码,它是独立的(创建一个数据容器)。要尝试它,请在窗体上添加一个 DataGridView。您可以在Button.ClickForm.Load 中插入代码以创建数据和绑定。查看结果,与您在此处看到的动画相同。顺便说一句,使用 DataGridView,当前项目会在您 EndEdit 单元格时更新,因此当您按 EnterTab 或以其他方式退出单元格时。
猜你喜欢
  • 2011-01-28
  • 2011-12-10
  • 1970-01-01
  • 2011-02-08
  • 2011-09-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多