Reputation: 334
I wrote some example code to demonstrate my question. one is bound to Object and other to DataRow:
Bind to DataRow example:
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;
}
}
}
It's look like:
As you can see, the binding to TextBox not work as it on next example. But, when I update the field by pressing on the button, the data grid refresh immediately:
Ok, now take look what hempen if I bind Object instead:
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; }
}
}
Now, the TextBox show the Id value as we aspect. But push on the Set button not refresh data on the DataGrid.
So, my question is:
Upvotes: 0
Views: 944
Reputation: 32248
Since, maybe, describing this procedure in the comments in not a great idea, here's the enlarged version.
Define a BindingList or a List<T>
as the data storage object (I prefer a BindingList
, but a simple List
, here, will do the job anyway).
Define a BindingSource that will provide change notifications and currency management. It greatly simplifies the binding of controls in WinForms (the class object should implement INotifyProeprtyChanged
, but for the purpose of this example is not important. It may become important in more specific scenarios, when you have two-way bindings that need to update the UI and the source of data immediately).
Set the BindingSource.DataSource property to the object that provides the data: here, a BindingList
or an IList
.
Add a Binding to the TextBox.Text
property which will be bound to a property of the Data source (or a Column of a DataTable, for example), setting the DataSource
value of the Binding
to the BindingSource
and the DataMember
value to the Property (or Column) of the data source to which the TextBox property is bound. Here, the Id
property of the Input
class.
Subscribe to the Parse event of the TextBox Binding
, to provide means to validate the data entered in the TextBox before allowing an update of the Data source. If the value entered doesn't fit the description (i.e., a user entered letters instead of numbers), we can call, for example, the BindingSource.ResetCurrentItem method to cancel the data update
Set the DataGridView.DataSource
to the BindingSource
.
This is what happens, using the code shown here:
Note:
I'm using a lambda here to subscribe to the Parse
event; you may want to use a separate handler if you need to subscribe/unsubcribe to this event more than once.
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;
}
Upvotes: 1
Reputation: 125197
Why binding TextBox not work correctly on the first example?
It's because the TypeDescriptor
of the DataRow
doesn't have a Id
property. Consider the following rules:
When data binding to a property of an item, the type descriptor of the item should contain a property with that name.
When data binding to a list, the list item type descriptor should contain a property with that name.
Is it true to say, that automatic (without any extra call for do binding) propagate update from source to control happens only with DataRow?
No. It's not because of DataRow
type. It's because of INotifyPropertyChanged
and IBindingList
. Consider the following rules:
When data binding a control to an item, if the item implements INotifyPropertyChanged
, then UI will be updated immediately after updating item.
When data-binding to a list control, if the item Implements INotifyPropertyChanged
and the list implement IBindingList
, the UI will be updated immediately after updating item or list.
More information
What I described above in short, you can find in details in Windows Forms Data Binding. I recommend reading the following useful documents:
Upvotes: 1