By Tim Daniels, ASNA US
This article provides an example of a Windows Form application that navigates between two forms and shows a way to pass data from a parent form to a child form. The program performs the following functions:
- Displays a list of customer names and numbers in a DataGridView
- Allows the user to select a customer by clicking a row header or data cell of the DataGridView
- Passes the selected customer information to another form that displays the selected customer information
A screenshot of the two forms is shown below in Figure 1.

Figure 1. The Customer List Form and the Customer Details Form
Two Windows forms are used in this application. The Customer List form contains a DataGridView to display a list of customer numbers and names. The second form, the Customer Details form, uses labels and text boxes to display the selected customer information.
Selecting a row in the DataGridView
The DataGridView is a useful component for displaying a list of data elements in a grid or spreadsheet style. This example uses two DataGridView events to display the child form:
- The RowHeaderMouseClick event. The DataGridView is by default rendered with a row header column (the clickable area at left edge of a row). The visibility of the row header is controlled by the RowHeadersVisible property of the DataGridView (which has a default value of true). When a row header is clicked, the RowHeaderMouseClick event is raised. This event’s e argument provides a RowIndex property that indicates what row was selected.
- The CellDoubleClick event. When a cell is double-clicked, the CellDoubleClick event is raised. This event’s e argument also provides a RowIndex property that indicates what row was selected.
Although the row header is handy, it’s not always intuitive to users to click only in this area to select a row, so it’s often a good idea to also observe the CellDoubleClick event for row selection. The event handlers for these two events are shown below. Because they both need the exact same action, they both call the ShowDetailsForm() method, passing it the row selected.
1 2 3 4 5 6 7 |
BegSr dataGridViewCustomer_RowHeaderMouseClick Access(*Private) + Event(*this.dataGridViewCustomer.RowHeaderMouseClick) DclSrParm sender Type(*Object) DclSrParm e Type(System.Windows.Forms.DataGridViewCellMouseEventArgs) ShowDetailsForm(e.RowIndex) EndSr |
1 2 3 4 5 6 7 |
BegSr dataGridViewCustomer_CellDoubleClick Access(*Private) + Event(*this.dataGridViewCustomer.CellDoubleClick) DclSrParm sender Type(*Object) DclSrParm e Type(System.Windows.Forms.DataGridViewCellEventArgs) ShowDetailsForm(e.RowIndex) EndSr |
Capturing the Selected Customer Information
The ShowDetailsForm() subroutine, shown below, is charged with capturing customer data, instancing the details form, and then showing it. The two lines that assign values to customerNumber and customerName are arbitrarily wrapped, using the -> symbol, in the figure below to avoid unusual text wrapping in the displayed text. In your code, these two lines should be two single lines of code.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
BegSr ShowDetailsForm DclSrParm RowNumber Type(*Integer4) DclFld customerNumber Type(*Packed) Len(9,0) DclFld customerName Type(*String) DclFld details Type(CustomerDetails) customerNumber = dataGridViewCustomer.Rows[RowNumber].-> Cells["CustomerNumber"].Value.ToString() customerName = dataGridViewCustomer.Rows[RowNumber].-> Cells["CustomerName"].Value.ToString() details = *NEW CustomerDetails(customerNumber, customerName) details.Show() EndSr |
The RowNumber parameter indicates what row was selected, then two intermediate fields are declared: customerNumber and customerName. These will be passed as arguments to the constructor of the CustomerDetails form. Another field called details is defined as an instance of the class CustomerDetails. Derived from System.Windows.Forms.Form, CustomerDetails is the child form.
Next, the two intermediate fields are populated with data from the selected row of the DataGridView. This is accomplished by interrogating two collections in the DataGridView; the SelectedRows collection and its Cells collection. Since only one row can be selected by clicking on the row header or a row, the selected row is at index zero of the SelectedRows collection. The customer number cell in the DataGridView is named “CustomerNumber” and the customer name cell is named “CustomerName.” These indices are used with the Row’s Cells property to fetch the corresponding values. The screenshow below in Figure 2 shows the DataGridView’s column properties where the column name is assigned (in this case you can see the CustomerNumber column in yellow).

Figure 2. The DataGridView’s column properties
The ToString () method is used to convert the object values of the DataGridView selected row into strings, before they are assigned to customerNumber and customerName. As long as the customer number string has all numeric data, AVR’s direct assignment operator (the = sign) coerces the source value to the *Packed target value type (just as the MOVE operation code would do). (If the implicitness of AVR’s assignment operator offends your convictions about writing strongly-typed code you can also manually convert the string value with .NET’s Convert.ToInt32() method.)
Finally, the *NEW keyword creates a new instance of the CustomerDetails form. The intermediate fields customerNumber and customerName are passed to the constructor subroutine of the details instance of the CustomerDetails class. The last line of code in the subroutine, details.Show (), executes the Show() method of the CustomerDetails Form class, which displays the form nonmodally. Because Show() is nonmodal, this means the user can display more than one instance of the CustomerDetails form concurrently. A user might want to do this to see two results side-by-side (for example, comparing two graphs). An additional feature of the Show() method is that the .NET runtime implicitly closes (or disposes) the form–no programmer cleanup required for forms displayed with Show().
Passing Data to the Customer Details Form
A way is needed to get the customer name and number to the Customer Details form. A way to do this is through the form’s constructor (which is a special-case subroutine, that every AVR class has available, that runs when the class is instanced). In some ways, the constructor is functionally similar to green-screen RPG’s *INZSR subroutine in that both are implicitly called at program (or class) startup time. The code below shows the Customer Details form’s constructor with the two necessary parameters added. In Windows System.Windows.Forms.Form classes you’ll notice that their constructors always have a call to InitializeComponent(). This routine writes up all of the internals of the controls on a Windows form. Always add your constructor-specific code after this line. In this case, the incoming customer number and name are assigned to corresponding textboxes on the form.
1 2 3 4 5 6 7 8 9 10 11 12 |
BegConstructor Access(*Public) DclSrParm customerNumber Type(*Packed) Len(9,0) DclSrParm customerName Type(*String) // // Required for Windows Form Designer support // InitializeComponent() textBoxCustName.Text = customerName textBoxCustNumber.Text = customerNumber EndConstructor |
This is but one pattern you can use to instance Windows forms and pass data to them. Future articles will look at others to accomplish this task.