This is a common question and often a solution is offered that uses some event of the DGV (such as CellContentClick), or clicking of a Button, to manually change the Text of TextBoxes. These solutions might answer the immediate question, but the opportunity should be taken to encourage the person to investigate, and pursue, data binding. The DGV should be bound to a data source, and TextBoxes to data members of this data source, so that they will automatically remain synchronised. The values displayed in the individual controls will automatically reflect the current row of the DGV without any additional event-code.
We need a new WinForms application with a DataGridView (DGV), named frmWithTextBoxes and dgvData. The three TextBoxes are named txtID, txtFirstName and txtLastName.

For this simple demonstration I manually create and populate a DataTable. In reality, there is likely to be a database (or other data store) and a DataAdapter is used to fill a DataTable or DataSet. See the example here:
How to: Bind Data to the Windows Forms DataGridView Control
Rather than binding directly to a data store the next step, and the more professional approach, is to use object-relational mapping to bind to a "virtual object database", perhaps using a framework such as Entity Framework. Essentially, programming happens with objects and properties which mirror the needed rows and columns of a database, without the need to write SQL statements directly against the database.
Please do not be distracted by the manual creation and populating of a DataTable for the purpose of this tutorial. The important purpose is to demonstrate binding the DGV to a data source of some kind, and binding other controls to the same data source so that they remain in sync.
Rather than binding directly to the DataTable I am binding to a BindingSource:
MSDN said:
The BindingSource component serves many purposes. First, it simplifies binding controls on a form to data by providing currency management, change notification, and other services between Windows Forms controls and data sources. This is accomplished by attaching the BindingSource component to your data source using the DataSource property. For complex binding scenarios you can optionally set the DataMember property to a specific column or list in the data source. You then bind controls to the BindingSource. All further interaction with the data is accomplished with calls to the BindingSource component.
Double-click an empty area of the form to go to the code-behind and create the Load event-stub.
Public Class frmWithTextBoxes
Private bsData As New BindingSource()
Private Sub frmWithTextBoxes_Load(sender As Object, e As EventArgs) Handles MyBase.Load
I only retain a class-level reference to the BindingSource. With a more detailed project references might also be kept to a DataSet and/or DataTable.
Within the Load event I then create a DataTable, define its columns, and add a few rows of data. Refer to the DataTable link provided earlier if you need information about this process, although the code here is fairly easy to follow.
' Create a new DataTable.
Dim table As DataTable = New DataTable("data")
Dim column As DataColumn
Dim row As DataRow
' Create first column and add to the DataTable.
column = New DataColumn()
column.DataType = System.Type.GetType("System.Int32")
column.ColumnName = "ID"
column.AutoIncrement = True
column.Caption = "ID"
column.ReadOnly = True
column.Unique = True
' Add the column to the DataColumnCollection.
table.Columns.Add(column)
column = New DataColumn()
column.DataType = System.Type.GetType("System.String")
column.ColumnName = "FirstName"
column.AutoIncrement = False
column.Caption = "First Name"
column.ReadOnly = False
column.Unique = False
table.Columns.Add(column)
column = New DataColumn()
column.DataType = System.Type.GetType("System.String")
column.ColumnName = "LastName"
column.AutoIncrement = False
column.Caption = "Last Name"
column.ReadOnly = False
column.Unique = False
table.Columns.Add(column)
row = table.NewRow()
row("FirstName") = "Bob"
row("LastName") = "Biscuit"
table.Rows.Add(row)
row = table.NewRow()
row("FirstName") = "Terry"
row("LastName") = "Tooltip"
table.Rows.Add(row)
row = table.NewRow()
row("FirstName") = "Mary"
row("LastName") = "Tattles"
table.Rows.Add(row)
Spoiler
After the above (still in the Load event) follows the most significant code:
Me.dgvData.DataSource = Me.bsData
Me.bsData.DataSource = table
The DataSource of the DGV is set to the BindingSource. The BindingSource hasn't been configured in any way yet, but it is with the second line:
Me.bsData.DataSource = table
The DataSource of the BindingSource is set to the DataTable.
After this, and essential for the purpose of this tutorial, the TextBoxes are bound to individual columns of the BindingSource (that is, columns of the DataTable):
Me.txtID.DataBindings.Add(New Binding("Text", Me.bsData, "ID"))
Me.txtFirstName.DataBindings.Add(New Binding("Text", Me.bsData, "FirstName"))
Me.txtLastName.DataBindings.Add(New Binding("Text", Me.bsData, "LastName"))
"Text" specifies that it is the Text property of the TextBox that is bound to the column-value.
Run the application. As you click between rows of the DGV the text in the TextBoxes mirrors the current row of the DGV. We haven't needed to write any additional event-code to achieve this.
You can edit a value using either the DGV or one of the TextBoxes, although you will have to move away from the row and back again to see the change reflected. More typically, the TextBoxes will display data that is not already visible in the DGV, or the BindingSource is configured to display data from a related, parent record, so this behaviour is not usually noticed or a concern.
With object-relational mapping, changes to values (to properties) can be immediately fed-back (notified) to the UI, without the need to move away from a row.
Using an Expression, Creating a Calculated Field
To display the fullname (firstname + lastname) in a TextBox, or as a column in the DGV, we first add a column to the DataTable using DataColumn.Expression.

column = New DataColumn()
column.DataType = System.Type.GetType("System.String")
column.ColumnName = "FullName"
column.Expression = "FirstName + ' ' + LastName"
column.AutoIncrement = False
column.Caption = "Full Name"
Table.Columns.Add(column)
(The ReadOnly and Unique properties cannot be set when using Expression, they are not relevant.)
(Note that it is the editor here at DIC that inserts the b-tag within the word Expression.)
Modify the existing code to the following:
Me.dgvData.DataSource = Me.bsData
Me.bsData.DataSource = table
'Me.dgvData.Columns("FullName").Visible = False
Me.txtID.DataBindings.Add(New Binding("Text", Me.bsData, "ID"))
Me.txtFirstName.DataBindings.Add(New Binding("Text", Me.bsData, "FirstName"))
Me.txtLastName.DataBindings.Add(New Binding("Text", Me.bsData, "LastName"))
Me.txtFullName.DataBindings.Add(New Binding("Text", Me.bsData, "FullName"))
Once you've tested this, uncomment the .Visible = False line if you don't want to display the fullname in the DGV.
You might notice that, if you only provide a firstname, the fullname box is empty. To correct this you can use functions such as ISNULL and IIF in the Expression. For example, "FirstName + ' ' + ISNULL(LastName,'')".
Full code, if needed:
Spoiler
This post has been edited by andrewsw: 23 October 2015 - 09:21 AM




MultiQuote


|