Consumers of Data on Windows Forms
For Windows Forms data binding to be possible, there must be data providers and consumers. It is easiest to approach the architecture of Windows Forms data binding from the provider side. For more information, see Providers of Data to Windows Forms.
The beginnings of binding a Windows Form and its controls lie in the providers of data. You can bind not just to traditional data sources, but also to almost any structure that contains data. An array that implements the IList interface, a collection, or one of the data structures from ADO.NET are all suitable data sources.
Any data source you bind a Windows Forms control to will have an associated CurrencyManager object. The CurrencyManager object keeps track of the position and otherwise supervises bindings to that data source. There is a CurrencyManager object on the form for each discrete data source that you are binding to. If the controls on the form all bind to a single source (for example, if several TextBox controls are bound to the same data table), then they will share the same CurrencyManager. However, there are times when controls on the form will be bound to different sources. In that case, there are multiple CurrencyManager objects on the form, each one keeping track of which record or data element is being used by the controls.
Additionally, every Windows Form has a BindingContext object. The BindingContext object keeps track of all of the CurrencyManager objects on a form. Thus, any Windows Form with data-bound controls will have at least one BindingContext object keeping track of one (or more) CurrencyManager object(s) keeping track of one data source (per CurrencyManager). If you use a container control, such as a GroupBox, Panel, or TabControl to contain data-bound controls, you can create a BindingContext for just that container control and its controls. This allows each part of your form to be managed by its own CurrencyManager object. See the BindingContext constructor for more details on creating multiple CurrencyManager objects for the same data source.
Note Just as you can bind properties of controls to a data source, you can also bind properties of controls to properties of other objects. For more information, see PropertyManager class.
Below is a diagram of how the Windows Form and its controls, the BindingContext object, the CurrencyManager object(s), and their respective data sources all maintain contact with one another.
The CurrencyManager is used to keep data-bound controls synchronized with each other (showing data from the same record). The CurrencyManager object does this by managing a collection of the bound data supplied by a data source. For each data source associated with a Windows Form, the form maintains at least one CurrencyManager. Because there may be more than one data source associated with a form, the BindingContext object manages all of the CurrencyManager objects for any particular form. More broadly, all container controls have at least one BindingContext object to manage their CurrencyManagers.
In Visual Studio .NET, data sources do not maintain a notion of a control's position within the data contained. In contrast, other data-access technologies, such as ADO, typically maintain a current record and provide ways for you to navigate to other records. Some people may be surprised to see that the data no longer "knows" where the current record is itself, due to the absence of a cursor. Now, without cursors, you can have multiple positions set on the same store of data.
Thus, an important property of the CurrencyManager is the Position property. Currency is a term used to refer to the currentness of position within a data structure. You can use the Position property of the CurrencyManager class to determine the currency of all controls bound to the same CurrencyManager.
For example, imagine a collection consisting of two columns called "FirstName" and "LastName". Two TextBox controls are bound to the same collection; the first is bound to the first column, and the second is bound to the second column. When the Position property of the common CurrencyManager is set to the fourth position within that list (corresponding to the fifth name, because it is zero-based), both controls display the appropriate values (the fifth "FirstName" and the fifth "LastName") for that position in the collection.
Each Windows Form has at least one BindingContext object that manages the CurrencyManager objects for the form. For each data source on a Windows Form, there is a single CurrencyManager object. Because there may be multiple data sources associated with a Windows Form, the BindingContext object enables you to retrieve any particular CurrencyManager object associated with a data source.
For example, if you add a TextBox control to a form and bind it to a column of a table in a dataset, the control communicates with the BindingContext object for that form. The BindingContext object, in turn, talks to the specific CurrencyManager object for that data association. If you queried the CurrencyManager's Position property, it would report the current record for that TextBox control's binding. In the example below, a TextBox control is bound to the FirstName column of a Customers table on the
dataSet1 dataset through the BindingContext object for the form it is on.
' Visual Basic TextBox1.DataBindings.Add("Text", dataSet1, "Customers.FirstName") // C# textBox1.DataBindings.Add("Text", dataSet1, "Customers.FirstName"); // C++ textBox1->DataBindings->Add(S"Text", dataSet1, S"Customers.FirstName");
To elaborate on the example above, you can add a second TextBox control (
TextBox2) to the form and bind it to the LastName column of the Customers table in the same dataset. The BindingContext object is aware of the first binding (
Customers.FirstName), so it would use the same CurrencyManager object, as both text boxes are bound to the same dataset (DataSet1).
' Visual Basic TextBox2.DataBindings.Add("Text", dataSet1, "Customers.LastName") // C# textBox2.DataBindings.Add("Text", dataSet1, "Customers.LastName"); // C++ textBox2->DataBindings->Add(S"Text", dataSet1, S"Customers.LastName");
Had we bound
TextBox2 to a different dataset, the BindingContext object would have created and managed a second CurrencyManager object.
Note It is important to be consistent about how you set the DataSource and DisplayMember properties; otherwise, the BindingContext object will create multiple currency managers for the same dataset, which results in errors. The following example shows a few ways to set the properties and their associated BindingContext objects. You can set the properties using either of the following methods — just make sure your method is consistent throughout your code.
' Visual Basic ComboBox1.DataSource = DataSet1 ComboBox1.DisplayMember = "Customers.FirstName" Me.BindingContext(dataSet1, "Customers").Position = 1 // C# comboBox1.DataSource = DataSet1; comboBox1.DisplayMember = "Customers.FirstName"; this.BindingContext[dataSet1, "Customers"].Position = 1; // C++ comboBox1->DataSource = dataSet1; comboBox1->DisplayMember = S"Customers.FirstName"; this->BindingContext->get_Item(dataSet1, S"Customers")->Position = 1; - or - ' Visual Basic ComboBox1.DataSource = DataSet1.Customers ComboBox1.DisplayMember = "FirstName" Me.BindingContext(dataSet1.Customers).Position = 1 // C# comboBox1.DataSource = DataSet1.Customers; comboBox1.DisplayMember = "FirstName"; this.BindingContext[dataSet1.Customers].Position = 1; // C++ comboBox1->DataSource = dataSet1->Customers; comboBox1->DisplayMember = S"FirstName"; this->BindingContext->get_Item(dataSet1->Customers)->Position = 1;