Matanya Cohen
Matanya Cohen

Reputation: 334

Binding control to Object vs DataRow

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:

enter image description here

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:

enter image description here

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:

  1. Why binding TextBox not work correctly on the first example?
  2. It's true to say, that automatic (without any extra call for do binding) propagate update from source to control happens only with DataRow?

Upvotes: 0

Views: 944

Answers (2)

Jimi
Jimi

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:

DataBindings BindingSource


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

Reza Aghaei
Reza Aghaei

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

Related Questions