VDF-GUIdance logo

  Visual DataFlex Logo

Shared knowledge leads to accumulated knowledge

        Printer Friendly Page

Create a header-detail construction

by Frank G. Vandervelpen


Frank has written some general guidelines on creating a basic header-detail construction in VDF
No Files Available
Date Created: 13/09/1999
Date Updated: 13/09/1999
Author: Frank G. Vandervelpen
Company: Velpe Development bvba

Header Detail Stuff

A lot of items relate from a child file to a parent file.
They typically appear as an order, invoice, delivery note or delivery addresses for a customer. The view is often defined as a section for the header and a table for each child record.

How to

Header Table

Step 1

Let's call the Header table INVHEAD and the detail table INVLINE
We need the dd's of both the header and detail table.
We will constrain the detail to the header
In the dd-object of the INVLINE in the view we add.
Set Constrain_File to INVHEAD.File_Number
This will give us the ability to view only the data in the INVLINE that corresponds to the active record.

Tip: You can dynamically change your constraints through the method «Rebuild_Constraints»
The view is build in this way
odd's (ģ ģ )
odbForms (ģ ģ )
The server and the Main_dd are the ones that are associated with the header table

Step 2

However the controlling of the detail should be through the detail file. We will change in the odbGrid the following:
Set Main_File to INVLINE.File_Number
Set Server to INVLINE_DD(Current_Object))
This makes the save pass through the dd of the detail data dictionary.

When we have such a view, we want to have a valid existing header record before entering the detail records...

Step 3

To have this behaviour we should do some coding.
Set Child_Table_State to True
If this property is true, the entering message sends the child_entering message and the exiting message sends the child_exiting message. These additional messages are provided for tables that are children of parent (header) tables.

This gives us the ability through the child_entering message to do some testing. One of the things we want is to check whether a header exists and/or to save it.
// The following is called when entering the table. It checks with the header 
// to see if it has a valid saved record. If not, entry is not allowed.

Function Child_entering returns Integer

    local integer liRVal
    // Check with header to see if it is saved.
    Delegate Get Save_Header to liRVal
    function_return liRVal // if non-zero don't enter in the detail


This should be placed in the odbGrid. There are some other methods that are added to this object for easier manipulation...
// Change: Assign insert-row key append a row Create new behavior to support append a row and to optimize the table refresh
on_Key kADD_MODE send append_a_row

// Add new record to the end of the table.
Procedure Append_a_Row // Q: how would a keyboard do this?

    Send End_Of_Data // A: Go to end of table and
    Send Down // down 1 line to empty line


// The way this table is set up, items can never be added out of order. 
// New items are always added to the end of the table. 
// By setting Auto_Regenerate_State to false we are telling the table to never bother reordering after adding records. 
// This is a minor optimization.

Set Auto_Regenerate_state to false // table is always in order.

We delegated a message Save_Header to the parent objects, but we did not define it, so in the odbView we need to add this procedure that passes the value if we may enter the detail lines.
Function Save_header returns integer

  Local integer liRec liChanged liSrvr
  Get Server to liSrvr //The Header DDO.
  Get Current_Record of liSrvr to liRec //The current order record #.
  Get Should_Save to liChanged //Are there any current changes?
  // If there is no record and no changes we have an error.
  If (liRec=0 AND liChanged=0) Begin //no record 
    Error 3xx 'You must First Create or Save Header'
    Function_Return 1
  //Attempt to Save the current Record 
  Send request_save_no_clear
  //The save succeeded if there are now no changes, and we have a saved record. 
  //Should_save tells us if we've got changes. We must check the data-sets
  //current_record property to see if we have a record. If it is 0, we had no save.
  Get Should_Save to liChanged //is a save still needed
  Get Current_record of liSrvr to liRec //current record of the DD
  //if no record or changes still exist, return an error code of 1
  If (liRec=0 OR liChanged) Function_return 1

This function will return whether the detail section can or can't be entered. These lines need to be added in the odbForm

Step 4

// Change: Create custom confirmation messages for save & delete. We must create the new functions and assign verify messages to them.

Function Confirm_Delete_Order Returns Integer

  local integer liRVal
  Get confirm "Delete Entire Header?" to liRVal
  Function_Return liRVal


// Only confirm on the saving of new records
Function Confirm_Save_Order Returns Integer

  local integer liNoSave liSrvr liRec
  Get Server to liSrvr
  Get Current_Record of liSrvr to liRec
  If liRec eq 0 Get Confirm "Save this NEW header?" to liNoSave
  Function_Return liNoSave


// Define alternate confirmation Messages

Set Verify_Save_MSG to GET_Confirm_save_order
Set Verify_Delete_MSG to GET_Confirm_delete_order


We can make custom save & delete messages for the header. These lines needs to be added to the odbForm
  • Header Table.
  • Constrain child to parent table.
  • Set child_table_state to True.
  • Check if you may enter the table in the odbGrid.
  • Ask odbForm if you may enter.
  • Make custom save and delete messages.