Professional Documents
Culture Documents
A Detailed Data Binding Tutorial
A Detailed Data Binding Tutorial
A Detailed Data Binding Tutorial
Demonstrates a variety of Windows Forms data binding features through several simple examples.
(This is a Visual Studio 2008 project. It contains two "Master-Detail" samples from this article.)
Introduction
The documentation for Windows Forms data binding is pretty sparse. How does it work, exactly? How much can you do with it? There are a lot of people who know how to use
data binding, but probably few who really understand it. I had a hard time merely figuring out how to use it, so I began to investigate it in the hopes of understanding this
mysterious beast.
I believe "data binding" has traditionally referred to automatic synchronization between controls and database rows or tables. In the .NET Framework (and Compact Framework
2.0), you can still do that, but the concept has been extended to other scenarios, to the extent that you can bind almost any property of any control to almost any object.
System.Windows.Forms.BindingSource is new in the .NET Framework 2.0. I have the impression Microsoft wants us to use BindingSource instead of older
classes such as CurrencyManager and BindingContext, so this article will only help you to use BindingSource.
Data binding can use Reflection, so you're not limited to database tables and rows in ADO.NET DataSets; rather, almost any object that has properties will work. For example,
1 of 30
data binding would be useful for implementing an "Options" dialog box if your options are held in an ordinary .NET object.
This is not a tutorial for ADO.NET/DataSets or DataGridView, but see Related Articles.
Note: In this article, I assume you are proficient in C# (and maybe ADO.NET), but you know nothing about data binding.
Disclaimer: I talk a little bit about .NET Compact Framework support for data binding, but I don't know whether everything described here is possible on that platform.
Contents
Introduction to data binding APIs
An airplane with passengers
The designer approach
The manual approach
How does it work?
What else can you do with data binding?
Filtering
A substring filter
The Control.DataBindings collection holds Binding objects, each of which has a DataSource property of type Object.
The DataSource property of ListBox, DataGridView etc. is of type Object.
The BindingSource class also has a DataSource of type Object.
So, what do these objects have to be? I find the documentation on this subject to be pretty confusing, which is why tutorials like this get written. In the real world, you will
probably use a BindingSource object as the the DataSource property of list controls and of Bindings. If you use databases, the DataSource property of your
BindingSource is usually a DataSet; otherwise, it may be an object of a class in your own application.
2 of 30
There seem to be a variety of different ways to do data binding, but I couldn't find them spelled out anywhere. So, I did a little experimentation to learn several of the things you
can do.
Let's start with the typical case: you assign a BindingSource object as the DataSource of a control. You can think of BindingSource as a "2-in-1" data source. It has:
1. a single object named the Current object. A property of a Control can be bound to a property of Current.
2. a List object that implements IList. The list should contain objects that have the same type as the Current object. List is a read-only property that either
returns a BindingSource's "internal list" (if the DataMember string is not set), or an "external list" used if DataMember is set. Current is always a member of
List (or null). When you set DataSource to a single (non-list) object, your List only contains that one item.
The way data binding works differs among different kinds of controls.
ComboBox and ListBox bind to the List through their DataSource and DisplayMember properties. You normally set the DataSource to a
BindingSource, and you set DisplayMember to the name of one of the attributes of the Current object.
DataGrid and DataGridView bind to the List through their DataSource property. DataGrid and DataGridView do not have a DisplayMember
property because they can display several properties from the data source (one in each column), not just one. DataGridView has an additional property called
DataMember, which seems to be analogous to the DataMember property of BindingSource. You would not normally set your grid's DataMember to anything
unless its DataSource is not a BindingSource (if you use a BindingSource, you would set BindingSource.DataMember instead.)
"Simple" controls such as TextBox, Button, CheckBox, etc., bind to individual properties of the Current object through the Control.DataBindings
collection. Actually, even list controls have a DataBindings collection, but it is not used as much. The DataBindings list can be changed in the designer under the
"(Advanced)" line under the "(Data Bindings)" node.
Tip: In this tutorial, we will often add a binding to the "Text" property of TextBoxes. Other common bindings you may add to DataBindings include:
Tip: On the desktop, Microsoft encourages you to use DataGridView, which is a more powerful "upgrade" of DataGrid. Only DataGrid is available in the .NET Compact
Framework, however.
Tip: The contents of ListView and TreeView cannot be data-bound (only simple properties such as "SelectedIndex" and "Enabled" can be bound). Some articles
on CodeProject propose ways to overcome this limitation, however.
class Airplane
3 of 30
{
public Airplane(string model, int fuelKg)
{
ID = ++lastID; Model = model; _fuelKg = fuelKg;
}
private static int lastID = 0;
public int ID;
private int _fuelKg;
public int GetFuelLeftKg() { return _fuelKg; }
public string Model;
public List<Passenger> Passengers = new List<Passenger>();
}
class Passenger
{
public Passenger(string name)
{
ID = ++lastID; Name = name;
}
private static int lastID = 0;
public int ID;
public string Name;
}
Well, sorry, but the above classes won't work. There's nothing to bind to here, because the data has to be provided in the form of properties, not methods or fields. And, I'm
guessing they have to be public non-static properties, though I haven't checked. So, let's try again:
class Airplane
{
public Airplane(string model, int fuelKg)
{
_id = ++lastID; Model = model; _fuelKg = fuelKg;
}
private static int lastID = 0;
public int _id;
public int ID { get { return _id; } }
public int _fuelKg;
public int FuelLeftKg { get { return _fuelKg; } set { _fuelKg = value; } }
public string _model;
public string Model { get { return _model; } set { _model = value; } }
public List<Passenger> _passengers = new List<Passenger>();
public List<Passenger> Passengers { get { return _passengers; } }
}
class Passenger
{
public Passenger(string name)
4 of 30
{
_id = ++lastID; Name = name;
}
private static int lastID = 0;
public int _id;
public int ID { get { return _id; } }
public string _name;
public string Name { get { return _name; } set { _name = value; } }
}
That's better. Suppose you put this in your project, and you want to have a DataGridView that shows a list of Airplanes. And, you want a TextBox where the user can
change the Model name of the currently selected Airplane. How?
5 of 30
Note: the wizard won't see Airplane and Passenger until the project is built.
You can tell this wizard to get the data from an "Object", and on the second page, you can select Airplane from a tree. The wizard will create a BindingSource in the
component tray and set its DataSource to typeof(Airplane):
6 of 30
The wizard also created three columns (DataGridViewTextBoxColumn objects) for three of the properties of Airplane. There is no column for the passengers list,
however; I guess the wizard knows you can't show a list of complex objects within a single cell. (You can show a dropdown list of strings within a cell, but I don't discuss that in
this article.)
In the properties of the TextBox, open up the "(Data Bindings)" node, click the "(Advanced)" line, then click "...". Choose the Text property, and then open up the "Binding" list
where you can select the Model property of airplaneBindingSource:
7 of 30
Then, click OK. Now, all you have to do is add some airplanes to the list. This is done by adding Airplanes to the BindingSource. So, make a Form1_Load() handler
(double-click an empty space on the form), and add some code like:
8 of 30
Tip: Notice that the ID field is read-only, so the DataGridView automatically prevents the user from changing it. Other controls are not so smart. For example, if the Model
were read-only, the user would still be allowed to change txtModel. To prevent this, you would have to (manually) set txtModel.ReadOnly = true.
All is well, but personally, I like to set up the binding in the code. So humor me. Let's start over. In the designer, delete airplaneBindingSource, and the controls will
suddenly be unbound again.
Note that we have to define our own BindingSource because we deleted the one in the designer. Anyway, run the program, and you should get the same thing as before:
9 of 30
Again, the DataGridView knows it can't create a column for Airplane.Passengers.
This was easier than using the wizards, wasn't it? With only five more lines of code, you no longer need to set it up in the designer. On the other hand, the designer makes it
easier to customize the columns.
An interesting thing I've noticed about data binding is that it's fairly forgiving about the order in which you do things. You can put the above statements in Form1_Load() in
any order, and the program will still work perfectly. Another interesting thing is that we don't have to tell BindingSource what kind of objects it will hold: you can take out
the assignment to DataSource (and the DataSource will stay equal to null), but internally, the BindingSource will still record the fact that the first object was an
Airplane. If you then add a non-Airplane to it, it throws InvalidOperationException.
By the way, DataSource is a weird property. Instead of typeof(Airplane), you could set it to an Airplane instead. Can you guess what happens if you replace the
three Add() statements with this?
Spoiler: you'll end up with two airplanes, the Airbus and the Cessna. It's almost as though you had written:
By the way, if you modify txtModel.Text in code, the Model of the current Airplane is not updated, at least not immediately (future events tend to trigger an update
somehow). One workaround is to change the underlying data (Airplane.Model) instead and then call bs.ResetCurrentItem() to update the controls.
10 of 30
How does it work?
Admittedly, the Model textbox is really not needed because you can edit cells in the Model column directly on the DataGridView. But, this example shows that the two
controls are automatically synchronized:
If you change the current row, the textbox automatically shows the model of the current row.
If you change the model text on one of the controls and press Tab, the other control is updated to match.
Magic! How do the two controls communicate? What's going on behind the scenes? BindingSource is the ringleader, actually. You can learn a little bit about it by reading
the documentation, which says that BindingSource "simplifies binding controls on a form to data by providing currency management, change notification, and other
services between Windows Forms controls and data sources". Currency management? When I saw that, I wondered, "isn't NumberFormatInfo in charge of that?" But, it turns
out that Currency management has nothing to do with Yen and Euros. Rather, it's Microsoft's way of saying "currentness". In other words, BindingSource keeps track of
which object is the current one in its List. Internally, BindingSource uses a CurrencyManager, which holds a reference to the list and keeps track of the current item.
When the user edits the model name, the control modifies the BindingSource.Current object somehow, and the BindingSource raises the
CurrentItemChanged event. Actually, a single modification raises multiple events, and if you want to find out which ones, just add this code to Form1_Load (this is C#
3.0 syntax; use anonymous delegates in C# 2.0):
But, how does the control notify the BindingSource about the change to its Text property? Remember, from a control's perspective, the DataSource is just an
Object. Also, I wonder: do controls have some kind of special support for BindingSource specifically, or would they accept other classes as long as they implement a
certain interface? Does BindingSource have some kind of special support for DataSet specifically, or does it merely look for certain methods/properties? In other words, is
data binding based on duck typing, or is it special-cased for certain classes or interfaces? And, what is expected from data sources, in general?
Well, by using Visual Studio 2008 and following these instructions, you can trace through the .NET Framework source code. And, there is a special tool with which you can do the
same thing in other versions of Visual Studio, plus, it downloads the complete source code rather than only the part you need. It is also possible to examine the code by
disassembling it with Reflector and the FileDisassembler add-in, but this approach does not let you trace through the code.
Unfortunately, it's a really enormous and complicated code. After several hours, I figured out how exactly a control notifies the BindingSource that its Text property
changed, and how the DataGrid is notified. I will now explain how, but the explanation is so convoluted you probably won't want to hear it. Feel free to skip a few paragraphs.
11 of 30
The Binding you added via txtModel.DataBindings.Add() has handlers attached to the TextBox's TextChanged and Validating events.
The latter handler (Binding.Target_Validate) passes the new value through a couple of internal classes, BindToObject and
ReflectPropertyDescriptor, the latter of which uses Reflection to actually change the value in the Airplane and then call base.OnValueChanged in
its base class, PropertyDescriptor.
OnValueChanged invokes a delegate associated with the same Airplane that points to BindingSource.ListItem_PropertyChanged.
This handler raises its ListChanged event (with a ListChangedEventArgs that specifies the index of the changed item).
CurrencyManager.List_ListChanged is attached to that event. This handler, in turn, raises its own ItemChanged event, to which
BindingSource.CurrencyManager_CurrentItemChanged is attached.
This event handler merely raises the BindingSource.CurrentItemChanged event, which calls our WriteLine("CurrentItemChanged") handler.
Next, CurrencyManager.List_ListChanged raises its own ListChanged event.
An inner internal class of DataGridView has a handler (DataGridViewDataConnection.currencyManager_ListChanged) that responds to that
event by refreshing the row that was changed.
Finally, currencyManager_ListChanged raises a DataBindingComplete event, but nobody handles it.
Whew! This is complicated. Now, how did the BindingSource manage to associate an event handler with the Airplane in the PropertyDescriptor associated with
the Binding in the TextBox's DataBindings collection? Well,
When you add the new Binding to the DataBindings collection, Binding.SetBindableComponent() is given a reference to the control, and the control
has a BindingContext, which is sort of a collection of bindings (similar but different from the DataBindings collection, apparently). So,
The Binding (let's call it "b") passes itself to BindingContext.UpdateBinding(). The documentation says that this method "Associates a Binding with a
new BindingContext". But, that's only true in a very roundabout way. First of all,
UpdateBinding() calls BindingContext.EnsureListManager(), which notices that the Binding's DataSource (which is a BindingSource,
remember) implements ICurrencyManagerProvider, so it calls ICurrencyManagerProvider.GetRelatedCurrencyManager(dataMember)
(where dataMember is associated with the Binding and, in this case, is an empty string). This is the magic part I was looking for--the part where the DataSource
is treated as more than just an Object. Here, we see that the .NET framework looks for a special interface rather than use duck typing (Reflection-based invocation).
Reflection is only used to learn the properties of Airplane.
At this point, BindingSource has the opportunity to return its own internal CurrencyManager object (let's call it "c") that is wired to the BindingSource.
However, now my head explodes because UpdateBinding() does not add b to the BindingContext, nor does it assign the CurrencyManager to b.
Instead, b is "added" to the CurrencyManager by calling c.Bindings.Add(b).
c.Bindings has the type ListManagerBindingsCollection (an internal class). c.Bindings.Add(b) calls a method that calls
b.SetListManager(c). In this way, the BindingSource's CurrencyManager is finally associated with the Binding b and its corresponding
BindToObject object. So, when the user changes the TextBox's Text and tabs away, b's BindToObject has access to the CurrencyManager, through
which it acquires a ReflectPropertyDescriptor (stored in BindToObject.fieldInfo). The ReflectPropertyDescriptor, in turn, was created
by ListBindingHelper.GetListItemProperties() on behalf of BindingSource during the first call to bs.Add() in our Form1_Load()
method. This ReflectPropertyDescriptor contains a mapping from our Airplane to BindingSource.ListItem_PropertyChanged so that the
BindingSource can be notified of changes to the Airplane.
Personally, I find this architecture highly unintuitive. And, the above was especially difficult to discover because the BCL (Base Class Library) is JIT-optimized, which means some
functions are inlined (missing from the call stack), and most variables cannot be seen in the debugger. Plus, Intellisense doesn't work in it. Plus, most of the code lacks comments.
But, I was at least able to determine that BindingContext (remember, there is a BindingContext for each control) has special-case code for data sources that
implement ICurrencyManagerProvider (which BindingSource implements), IList, and IListSource, so you can expect that your data source must
12 of 30
implement one of these interfaces in order to act like a list.
As for BindingSource, it creates a List property of type BindingList<T> where T is the kind of data you have given to it (e.g., BindingList<Airplane>). It
does not seem to be special-cased for DataSet, although some code is special-cased for some interfaces, e.g.:
grid.DataSource = bs;
grid.AutoGenerateColumns = true;
txtModel.DataBindings.Add("Text", bs, "Name");
label1.Text = "Name:";
}
Unlike last time, this time we set the DataMember property. Now, you get a list of passengers for a single Airplane, instead of a list of Airplanes:
13 of 30
Tip: If you want to show an ADO.NET table, just replace Airplane with your DataSet and replace "Passengers" with the name of a DataTable in that DataSet.
Notice that we can either add items to the BindingSource (bs) or to a.Passengers directly. But, changing a.Passengers directly doesn't always work, as you'll see
if you add this code to the end of Form1_Load():
EventHandler eh = null;
Application.Idle += (eh = delegate(object s, EventArgs e2) {
// Window is now visible
a.Passengers.Insert(0, new Passenger("Oops 2"));
Application.Idle -= eh;
});
There will still only be four items in the list. At first, you see Oops 1 at the top; Oops 2 will suddenly appear when you move your mouse over the grid. It doesn't work correctly
because the BindingSource was not notified of the changes. If you must modify the underlying list, you can refresh the on-screen list by calling
BindingSource.ResetBindings(false).
You can bind to an object directly without a BindingSource. For this example (and only this example), add a Button (button1) to the form. At run-time, it will look like
this:
14 of 30
In this example, you can edit Airplane.Passengers and Airplane.Model.
To set it up, double-click button1, then replace Form1_Load and button1_Click, with the following code:
grid.DataSource = a;
grid.DataMember = "Passengers";
grid.AutoGenerateColumns = true;
txtModel.DataBindings.Add("Text", a, "Model");
}
15 of 30
button1 demonstrates two things:
When the user changes the grid or text box, the underlying data source is still changed as you would expect.
If you add a row to the underlying data source, you must call ResetBindings() on the grid (this is not required when adding a row to a BindingSource).
This example seems to work perfectly fine, so why bother to use a BindingSource at all?
A BindingSource automatically synchronizes data between multiple controls that show the same data. This example only works right because the data on the two
controls is independent.
A BindingSource automatically refreshes bound controls when you add or remove an item from its List.
BindingSources can be chained together (as discussed in the next section).
Something strange happens when you modify the model: the grid's CurrentRow.Index changes to zero. I have no idea why, but I doubt this occurs when you use a
BindingSource.
If a DataSet or DataTable is used as a DataSource, I heard they provide some, but not all, of these features (I don't know the details).
Note: we won't use the button anymore, so you can delete it now.
You can show a list within a record in a ListBox or ComboBox. Let's say you want to show the passengers on the selected plane, in a new list named "lstPassengers",
so that your window looks like this:
16 of 30
{
// Create some example data.
Airplane a1, a2, a3;
bs.Add(a1 = new Airplane("Boeing 747", 800));
bs.Add(a2 = new Airplane("Airbus A380", 1023));
bs.Add(a3 = new Airplane("Cessna 162", 67));
a1.Passengers.Add(new Passenger("Joe Shmuck"));
a1.Passengers.Add(new Passenger("Jack B. Nimble"));
a1.Passengers.Add(new Passenger("Jib Jab"));
a2.Passengers.Add(new Passenger("Jackie Tyler"));
a2.Passengers.Add(new Passenger("Jane Doe"));
a3.Passengers.Add(new Passenger("John Smith"));
Here, we tell the ListBox to be populated with the Name property of all Passengers using a dot-separated notation. (I am not sure whether you can use dot-separated
names in other circumstances).
But, what if you want to use data binding with a TextBox so the user can change passengers' names?
Design time
17 of 30
Runtime
This kind of data binding is known as Master-Details. For this, you need two BindingSources, because a BindingSource only has one CurrencyManager, so it can
only keep track of one "current record". Here, you need two because you want txtModel bound to the current Airplane and txtName bound to the current
Passenger. Luckily, the solution is easy because you can chain BindingSources together:
18 of 30
bsP.DataMember = "Passengers";
lstPassengers.DataSource = bsP;
lstPassengers.DisplayMember = "Name";
txtName.DataBindings.Add("Text", bsP, "Name");
}
The DataGridView might not provide a way to add new rows* even though its AllowUserToAddRows property is true by default. This is because
BindingSource's list (BindingList<Airplane>) has a property that must also be true in order to add rows. If you add the following line at the end of
Form1_Load, DataGridView will provide a row-adding interface:
((BindingList<Airplane>)bsA.List).AllowNew = true;
((BindingList<Airplane>)bsA.List).AllowRemove = true;
But, the user interface for this is not intuitive. The user must select the whole row by clicking the little rectangle to the left of the row, then press the Delete key. There is no way to
delete a row using only the mouse or only the keyboard.
* I'm puzzled. The first time I ran this example, AllowNew was false by default. Later in the day, I tried the same code again (or so I thought), but AllowNew was true by
default. Go figure.
As scenarios become more complex, the more likely it is you'll want to use a database, or at least a DataSet.
The following example is like the previous example, but uses a DataSet instead of Airplane and Passenger classes. I've constructed the DataSet's schema manually in
order to spare you the trouble of setting up a database or a typed DataSet. Simply copy and paste this method into Form1:
DataSet CreateAirplaneSchema()
{
DataSet ds = new DataSet();
19 of 30
a_id.AutoIncrement = true;
a_id.AutoIncrementSeed = 1;
a_id.AutoIncrementStep = 1;
return ds;
}
And, use the following Form1_Load() code (lines changed from the previous example are marked with //**):
20 of 30
passengers.Rows.Add(null, a2["ID"], "Jackie Tyler"); //**
passengers.Rows.Add(null, a2["ID"], "Jane Doe"); //**
passengers.Rows.Add(null, a3["ID"], "John Smith"); //**
When you run the program, it behaves the same as before, except that the user can sort the list, add new rows, and delete rows, by default:
Image 11
(If you don't think the user should be able to add rows, just set the grid's AllowUserToAddRows property to false. While you're at it, you might want to change
AllowUserToDeleteRows, AllowUserToOrderColumns, AllowUserToResizeColumns, and AllowUserToResizeRows also. But I digress: this is not a
DataGridView tutorial.)
Normally, bsP.Current points to a DataRowView if your DataSource is a DataSet; in the earlier object example, it normally points to a Passenger. But, when you
create a new row, it has no passengers, so bsP.Current is null. I have not seen any documentation about how the binding architecture is supposed to behave when there is
no "current" item, but at least the architecture is smart enough to clear the "Name" textbox. However, you might notice that the user can still change the name (which has no
effect).
If you want to disable the textbox when there are no passengers, you could add a handler for the binding source's ListChanged event:
...
...
private void Form1_Load(object sender, EventArgs e)
{
bsP.ListChanged += new ListChangedEventHandler(bsP_ListChanged); //**
21 of 30
void bsP_ListChanged(object sender, ListChangedEventArgs e) //**
{ //**
// ListChangedType.Reset indicates that the entire list changed. //**
// ListChanged is also raised when rows/columns are added/removed. //**
if (e.ListChangedType == ListChangedType.Reset) //**
txtName.Enabled = bsP.Current != null; //**
} //**
I don't know whether this is the simplest solution, but it works fine.
Filtering
In a "real-world" application, there may be hundreds of rows to display. What if you want to filter the list according to some criteria given by the user?
BindingSource provides a "Filter" property that allows you to specify a boolean expression that controls which rows are displayed on bound controls. However,
BindingSource itself does not evaluate this expression; it just passes it along to the underlying List, which must implement IBindingListView. In our object-based
example, the list of Airplanes was BindingList<Airplane> and the list of Passengers was a List<Passenger>. Neither of those classes implement
IBindingListView, so that example can't use filtering (although filtering and sorting support could be added with BindingListView, an open-source library.)
You can, however, filter DataTables and DataViews (DataTable itself doesn't implement IBindingListView, but its DefaultView, returned by
IListSource.GetList(), does.)
To demonstrate DataSet-based filtering, I created a new Form, Form2, based on the hierarchical DataSet example above. Then, I added two new TextBoxes,
txtAirplaneFilter and txtPassengerFilter (and some labels) to get this:
Image 12
Next, I added the following TextChanged event handlers for the TextBoxes:
22 of 30
txtPassengerFilter.BackColor = SystemColors.Window;
} catch(InvalidExpressionException) {
txtPassengerFilter.BackColor = Color.Pink;
}
}
Here it is in action:
Image 13
As the screenshot shows, DataSet supports an SQL-style syntax for filter expressions. If the expression is not understood, the TextBox will have a pink background. By the
way, setting the filter to an empty string clears the filter, almost as though you had called RemoveFilter() on the BindingSource. And, the
DataSet.CaseSensitive property controls whether string tests are case sensitive.
Tip: if you have two lists that are bound to different BindingSources, but each BindingSource is attached to the same DataTable, then both lists share the same
filter (or so I've heard). To give them independent filters, create two DataViews attached to the DataTable (via the Table property), and set each BindingSource's
DataSource to a different DataView.
A substring filter
Normally, you will not make your user input complete filter expressions. Instead, you might show all records that contain a substring that the user inputs. How could this be done?
It would be nice if you could use a delegate as a filter, but the DataView does not support it. You must work with the operators that the filter string offers, and the closest thing
to a substring search is the "LIKE" operator. An obvious choice of filter strings would be:
Unfortunately, this may not work as the user expects, e.g., if the user puts an apostrophe in his/her filter string. I offer this escaping routine to help:
23 of 30
}
return s.ToString();
}
Maybe, I want to have a Cancel button that aborts all changes to a record. Or maybe, changes shouldn't be kept unless a "Save" button is clicked. How can this be
accomplished?
Maybe, I want to let the user edit multiple records simultaneously, taking advantage of the multiple selection feature of DataGridView, ListBox, and other bindable
controls. How could I, for example, let the user select multiple rows and then set the model of several airplanes simultaneously (to the same string)?
Related articles
MSDN Forum: Windows Forms Data Controls and Databinding (look here for DataGridView FAQs)
Presenting Data with the DataGridView control (a detailed tutorial)
Using ADO.NET for beginners
Managing an @@IDENTITY crisis (dealing with AutoIncrement and Identity columns in ADO.NET and SQL server)
ADO.NET for the Object-Oriented Programmer
Data Bound TreeViews one and two
24 of 30
What
is
a
BindingSource
and
why
do
I
need
it?
101
Ways
to
Manipulate
the
DataGridView
Control
Complex
data
binding
a
collection
implementing
IBindingList
and
ITypedList
Data
Binding
in
.NET
/
C#
Windows
Forms
Using
DataSets
in
ADO.NET
Differences
Between
the
Windows
25 of 30
Forms
DataGridView
and
DataGrid
Controls
License
This
article,
along
with
any
associated
source
code
and
files,
is
licensed
under
The
MIT
License
About
the
Author
Qwertie
Software Developer None
Canada
Since
I
26 of 30
started
programming
when
I
was
11,
I
wrote
the
SNES
emulator
"SNEqr",
the
FastNav
mapping
component,
the
Enhanced
C#
programming
language
(in
progress),
the
parser
generator
LLLPG,
and
LES,
a
syntax
to
help
you
start
building
programming
languages,
DSLs
or
build
27 of 30
systems.
My
overall
focus
is
on
the
Language
of
your
choice
(Loyc)
initiative,
which
is
about
investigating
ways
to
improve
interoperability
between
programming
languages
and
putting
more
power
in
the
hands
of
developers.
I'm
also
seeking
employment.
28 of 30
Comments
and
Discussions
80
messages
have
been
posted
for
this
article
Visit
https://www.codeproject.com
/Articles
/24656
/A-
Detailed-
Data-
Binding-
Tutorial
to
post
and
view
comments
on
this
article,
or
click
here
to
29 of 30
get
a
print
view
with
messages.
30 of 30