You are on page 1of 235

Friday, November 13, 2020

Learning Clarion (Part 1)

The Getting Started section briefly introduced you to Clarion programming at its
highest level. Learning Clarion now teaches you how you can use all the rest of the
tools Clarion provides to create real-world applications. It contains two parts, on two
very different levels:
• A series of Application Generator lessons, which familiarizes you with all the tools
in the Clarion development environment.
• A Clarion Language lesson, which introduces the Clarion programming language
and familiarizes you with the type of code generated for you by the development
environment.
Lessons 1 through 12 introduce all the main Clarion development environment tools.
We start at the application planning stage, walk you through creating the data
dictionary with the Dictionary Editor, and then walk you through creating a complete
application with the Application Generator. By the end of these lessons you’ll have
created a complete order-entry application.
You’ll use the Application Generator and work with Procedure, Control, and Code
templates to produce an Order Entry application. You’ll work with the Window
Designer to design windows. You’ll work with the Report Designer to design reports.
You’ll use the Text Editor to embed Clarion language source code into the code
generated by the templates.
Lesson 13 introduces the Clarion programming language at the hand-coding level. It
starts at "Hello World", then walks you through creating simple forms of the most
typical types of procedures used in Clarion applications, all while explaining the
functionality of the hand-code you’re writing and relating it to the type of code you’ll
see generated for you by the Application Generator.
Please review the section entitled "Anatomy of a Database" in the help file. We
covered it in the Introduction to Clarion blog entry, where you read it in the
"Learning Clarion" PDF file. This time it's in the help file, so it should already be
familiar territory.
In summary:
• A database is a collection of information (data) in a system of columns (fields),
rows (records), and tables.
• Columns (fields) can store many different types of data, but each individual
column is specified to hold only one type.
• Each data item should be stored in only one place.
• One or more columns makes up a row. One or more rows make up a table. A
collection of related tables make up a database.
• Clarion programs can access many different table systems through the use of table
drivers.
• Keys and indexes declare sort orders other than the physical order of the rows
within the table, and can contain more than one column, allowing sorts within
sorts.
• Range Limits enable you to process a subset of rows, which reduces processing
time.
• Tables are related through common columns containing identical data, which
allows you to eliminate redundant data.
• Table names and column (field) names should be singular, not plural, in almost
all cases.
• A primary key is a column, or combination of columns, that uniquely identifies
each row in a data table.
• A foreign key is a column, or combination of columns, in one table whose value
must match a primary key’s value in another related table.

Lesson 1: Planning the Application


As a general rule, every minute you spend planning your application beforehand
saves you ten minutes later. This topic informally describes the planning process for
the application you’ll create in the subsequent lessons. In the real world, you’ll
probably create a bona fide functional specification for your important applications.
This informal description defines:
• The tasks the application performs.
• The data the application maintains, and how it stores it.
As a starting point, this Application Generator lesson application uses the data
dictionary from the applications you created in the Getting Started manual. It extends
the concept to a simple Order Entry system, using the database for keeping track of
customers and what they ordered.

Defining Application Tasks


This application will maintain the customer and billing tables for a manufacturing
company. The first task in planning just what the application will do is to assess what
the company expects it to do. For the subsequent lessons, the application we’ll create
is a simple order entry system. Customers typically phone in orders for one or more
products at a time. A salesperson takes the order. The billing department prints an
invoice that night.
The application therefore must provide:
• Entry dialogs for taking the order, or modifying the data in it later.
• Access to the customer list from within the order entry dialogs. The customer list is
the one you created in the Getting Started lessons, stored in the Customer table.
• Access to the list of part numbers (items) that the company manufactures, from the
order entry dialogs.
• Browse windows for listing sales transactions.
• Procedures that will maintain the Product list and Customer information.
• Printed reports.

Designing the Database


The first task in planning the table structure is to assess what data the application
needs, and how to store it with the minimum amount of duplication. Good database
management maintains separate data tables for each "entity" or group of discrete
data elements. The data "entities" this application maintains are:
Customer
Customer name and address data that changes only when a customer moves. Created
in the Getting Started lessons, along with its related Order table.
Phone
In the communication age that we live in, it is probable that a customer may have
more than one contact phone number.
Order
Basic information needed for assembling the data needed to print an invoice. It
"looks up" information from the other tables, such as the customer name and
address. When a sales person takes a new order, they add a row to this table.
Order Detail
Product, price, and quantity ordered for an item on a given invoice: the variable
information for each order. Though this duplicates price information in the Product
table, you must maintain the price at the time of the sale here. Otherwise, when you
increase the price in the Product table, it would cause the balance in the Detail table
to change.
Product
Information on the products sold by the company, including product number,
description and price. This data changes only when a price changes or a new product
is added.
State
The list of states in the USA where the customer is located.
The Customer Table
The Customer table stores "constant" data such as customer names and addresses.
It’s most efficient to store this data in one place, allowing for a single update when the
information changes. This also saves space by eliminating redundant customer
information in the Order table; otherwise, if there were 1000 orders for company
XYZ, the address information would be repeated 1000 times. Reducing storage
requirements by storing the data only once is called normalization.
The customer data requires a column to uniquely identify the customer. The
company name is unsuitable because there could be duplicates, or it may change.
There may be, for example, multiple rows for a customer called "Widget Depot" if it
has multiple locations. The CustNumber column is a good candidate for an auto-
number key which automatically creates and stores unique customer numbers.
The CustNumber column also serves as the primary key for the data table. Any other
data tables which are related to the Customer table must declare the CustNumber
column as a foreign key.
Because there may be many orders for each customer number, the relationship
between the Customer table and the Order table will be a one to many (1:Many)
relationship. We say the Customer data table is the parent table, and the Order data
table is the child table.

The Phone Table


The Phone table stores telephone numbers; each customer could have several. Each
row includes a CustNumber column to relate back to the Customer table.
The Phone table also includes a text column in which we can indicate whether the
phone number is an office, fax, mobile or home number. Using the data dictionary,
we’ll specify that the control for entering data for this column should be a drop-down
list with the choices already loaded.

The Order Table


The Order data table gathers information for each sales transaction from all the other
data tables (such as the customer data). Because much of the basic data in this table
prints in the "header" area of the invoice, this is sometimes called the Order Header.
Every sales transaction requires one row in the Orders table. The row relates to the
customer information by referencing the unique customer number. Because some
order rows may reference one product, and others may reference ten, you’ll create a
separate Detail table which relates back to a unique order number. This creates a one-
to-many relationship, with the Order table as parent and Detail as child. The actual
products ordered are identified by their product codes, in the Detail table.
The Order row thus holds a customer number to relate back to the customer data (the
foreign key), and a unique order number to relate to the Detail. You’ll create a multi-
component primary key on the two columns so that you can easily create a browse
sorted by customer and invoice number.

The Order Detail Table


The Detail table stores the products ordered by their product codes (a foreign key into
the Product table), their individual prices, the quantity of each, and the tax rate. An
additional column holds an invoice number, which relates back to the Order table in a
many-to-one relationship.
The Detail table duplicates the price information with the columns in the product
table; this is because prices may change. It’s important to store the price column
within the detail table row because if the price increases in six months, today’s "paid
in full invoice" would reflect a balance due.

The Product Table


The Product table stores unique product numbers, descriptions, and prices. When the
sales person looks up a product by name, the application inserts the product number
into the Detail row. The product code is the primary key: no two items can have the
same code, and every product must have a code. An additional column contains the
tax rate for the product.

The State Table


The State table is a list of the two-letter codes for the states in the USA where the
customer is located. Since there are only two fields in the table, this is often referred
to as a "lookup table".

Referential Integrity
Referential Integrity refers to the process of checking all updates to the key column in
a given table, to ensure that the validity of parent-child relationships is correctly
maintained. It also refers to ensuring that all child table rows always have associated
parent rows so that there are no "orphan" rows in the database.
Because the data for a given transaction resides across several tables, this application
must enforce referential integrity. This is critical, yet many database application
development tools require you to hand code procedures to enforce this. The
Application Generator’s templates implement this automatically in your generated
source code when you select a few options in the Data Dictionary.
It is essential that the application does not allow an update to a row that leaves a
blank or duplicate value in a primary key column. For example, we need to restrict
the ability of the end user to update a row in a way that could cause a duplicate
Customer number. If two different companies shared a duplicate Customer number,
you could send a bill to the wrong company.

The Complete Database Schematic


The schematic below is supposed to provide an overview of the entire database.

In my opinion, this diagram is a confusing mess. The boxes are too close together,
and the arrangement is jumbled, so it takes a while to figure out what is related to
what. Also, some table names are in ALL CAPS and many are plural words, which
makes things confusing.
If you are going to spend time designing the database correctly, spend a little more
organising the data diagram properly too. Try to put the "one" tables on the left, and
the "many" tables on the right. This immediately makes everything more easy to read.
Sloppy thinking leads to errors: always aim to be clear and precise.

This diagram shows more clearly what is going on. I have highlighted the Primary
Key (PK) columns in yellow. This immediately shows up two design flaws in the
database design.
Firstly, there is no PK in the Phone table. Secondly, the Detail table has problems. If
we make the PK OrderNumber + ProdNumber, then we can only sell the product
once in a given order. What about selling 10 items at the special price and 2 items at
the standard price? Also, the Products will not show up on the Order in the same
sequence as ordered by the Customer. This could cause problems in the real world. It
is best to resolve these issues at the design stage, and not wait until after you have
shipped the application to the customer.
The schematic above provides an overview of the entire database. If you look at it
from the perspective of the sales agent taking a phone order, the Order table stores
who’s ordering, the Detail stores what they’re ordering, and the Customer and
Product tables store constant information about the customers and products.
The item code looks up the description and price. The customer code looks up the
customer’s name and address. Other data, such as the transaction date, fill in
automatically (by looking up the system date, for example).
The lessons will create a brand new data dictionary, and you will copy and paste the
tables that Getting Started defined for you into the new dictionary.
As for the actual application you create, because the lessons are a teaching tool more
concerned with showing what Clarion can do for you, it won’t create a full-scale order
entry system. However, you will find that some parts of the application will be very
"showy," so that you can quickly learn how to do equivalent procedures in your
applications.

Application Interface
The next major task before coding is to plan the user interface. For a business
application like this, it’s crucial that a salesperson quickly locate the data they need,
so that they can record the sale and move on to the next phone call. Therefore, the
application should put all the important data "up front" by default, and no entry or
maintenance dialog should be more than a button or menu command away.
Additionally, the company uses many other Windows applications; therefore, it’s
especially important that the application have a standard Windows "look and feel."
End users learn a familiar interface more quickly.
To implement the tasks the application must execute consistently with our
guidelines, we can plan for the items listed below. Though the following is no
substitute for a real program spec, it should suit us for the upcoming lessons.
• Because the application will handle the maintenance for the customer, item, and
billings tables on different forms, the Multiple Document Interface (MDI) is
necessary.
• The application should have a toolbar with buttons to load forms and browse
windows, and to control the browsing behaviour.
• To maintain a consistent "look and feel," the main menu choices will be File, Edit,
View, Window, and Help. The File menu accesses the printing and exit procedures.
The Toolbar buttons call the form dialogs for editing a current row (if highlighted
in a browse) or adding/deleting rows, and for browsing through the tables. The
Browse menu calls the procedures for browsing tables. Window and Help perform
standard actions.
• When adding new orders, the sales people should be able to pick customers and
products from scrolling lists. Pertinent data in the order dialog (addresses, item
descriptions and prices) should automatically "fill in" as appropriate.

OK, What Did I Just Do?


Here’s a quick recap of what you just accomplished:
• You defined the tasks the Application must accomplish.
• You designed the database that will allow the application to accomplish those
tasks.
• You specified the user interface the application will use.
Now that the application description is fairly complete, we’re ready to begin work.
The first step is to create the data dictionary.

Lesson 2: Creating the Data


Dictionary
Start the Clarion IDE, and go to the "Dictionaries" tab in the "Start Page". Click on
"New Dictionary". Save the file as "LCLesson.dct" in the following folder:
"C:\Users\Public\Documents\SoftVelocity\Clarion11\Lessons\LearningClarion"

From the "Dictionary" menu, click on "Show Properties Dialog".


Click on the "Add" button and type in "Learning Clarion Lessons Dictionary" as the
comment. Click "OK". Then click on the "Versions" tab and note that this is the initial
version. These two features (comments and versions) allow you to make notes and
keep track of changes to the dictionary over the life of the application. Click "OK".

Copying Tables From One Dictionary to Another


You can use the standard Windows copy and paste commands to copy table
definitions from another dictionary (or to copy columns from one table to another).
In other words, once you’ve defined it once, why bother to re-define it when you can
just copy what you’ve already done?
There is also a better and recommended way to copy table definitions from
one Clarion Dictionary to another, by using the "Table Import Wizard". We will use
this better method in this exercise.
Click on the "Add Table" button in the DCT Explorer, and select "Import Tables".
Click "Yes" if prompted to save changes to the existing dictionary.

Make sure that "Select Server" says "Clarion Dictionary". Click on the ellipsis button
next to the "Select Dictionary" field, and navigate to the "Getting Started" folder to
find "GSLesson.dct". Click "Open".

Click on the "Add All" button, and then click "Finish". All the table and field
definitions are imported, including their relationships. By contrast, using
the Windows clipboard to copy and paste doesn't transfer the relationships, only the
table structures.
Note that in the Properties section of the Customer table, the "Full Path Name" is still
showing the old "Getting Started" path. Just remove this name entirely.

Last time we changed the description fields for each column to mixed case. This time
we are going to change the Column Names as well, as shown above.

When you get to the "State" field, go to the "Attributes" tab as well, and choose
"Uppercase" and set the initial value to 'FL', including the quote marks. This will
default the state code to Florida for new customers. Don't forget to change the case of
the "ZIPcode" field too. Then click on the "Save" button.
The initial value in this case is a string, hence the single quotes. You can also use
variable names (Preceed with an exclamation mark) or functions.
Repeat the process of removing the full Path Name and changing the case of the
column names in the "Order" table, as shown above. Then go to the "OrderDate" field
and set the "Initial Value" as the function TODAY() as shown.
Click on the green "Save and Close" button to return to the "Start Page".

Instead of clicking on the LCLesson file, click on the path to the right, as shown. This
will open the "LearningClarion" folder.

Please rename "STATES.TPS" to "STATE.TPS" and "ORDERS.TPS" to "ORDER.TPS".


This is so the TopSpeed file names match the table names in the dictionary. Close the
folder and return to the "Start Page".
Make a backup of your work.

OK, What Did I Just Do?


Here’s a quick recap of what you just accomplished:
• You created a new, empty data dictionary (.DCT file).
• You imported existing table definitions from one data dictionary to another (the
easy way to work: never re-invent the wheel).
• You added an initial value for one of the columns in the database.
In the next lesson, you’ll learn how to add a table to the data dictionary, starting
totally "from scratch". It's quick and easy it is to do even without using a wizard.
Learning Clarion (Part 2)

This follows on from Learning Clarion (Part 1)

Lesson 3: Adding Tables and Columns


This lesson teaches you how to:
• Create new table definitions.
• Create a pool of column definitions from which new column definitions can be
easily derived.
• Create additional column definitions for all tables.
A label may only contain letters, numbers, and the underscore(_) or colon (:) characters,
and must begin with a letter or underscore. Executable code statements use this label to refer
to the table or field.

Defining New Tables


We have already copied and modified the three tables defined in the Getting Started
dictionary. Now we are ready to add a new table from scratch. Please open the LCLesson
dictionary file.

Click on the "Add Tables" button and fill in the details as shown for the Phone table. The
prefix is automatically derived from the first 3 letters of the table name. Since the other
prefixes are all in caps, change this one to caps too.
The prefix is one way to uniquely identify columns of the same name in different tables. For
example, PHO:CustNumber is the CustNumber column in the Phone table, while
CUS:CustNumber is the CustNumber column in the Customer table. You can also uniquely
identify columns by using Field Qualification syntax (discussed in the Language Reference).
Click on the "Comments" tab and add in some further information about this table:
"Customer Phone table". Comments are a helpful way of documenting your data structure. A
description of what the table is for can be very helpful for when you return to the table for
maintenance programming. Click "OK" to add the table.

Use the "Add Table" button to create the Detail table. Because it is a child table, it is
sometimes useful to include the parent table name in the description, as shown here: "Order
Details". We do not have to stick to the default prefixes, and they are not restricted to 3
letters. You could change it to "DTL" as shown, but we will leave it as "DET" for future
lessons. Click "OK" when done.
Use the "Add Table" button to create the Product table as shown. Leave the prefix as "PRO".
Click "OK" when done.
Save your work using the "Save" button.

Defining the Columns


Several tables use related fields, so it makes sense to create a pool of field definitions that we
can use in multiple tables, to ensure that they are all consistent. Fields like "CustNumber"
and "OrderNumber" are good candidates because they occur in several tables.

Click on the "Pools" folder, and use the "Add Pools" button to create "ColumnPool" with a
prefix "POOL" as shown above. Click "OK". Pools do not generate any code.
We are going to copy the pool columns from their existing tables and add them to the pool by
pasting them.
Select the CustNumber field, right-click and choose "Copy".

Click on "ColumnPool", then right-click on "Columns" and choose "Paste".

Return to the "CustNumber" field in the "Customer" table, and fill in the "Derived From" field
as "POOL:CustNumber". Then click on the ellipsis button to the right.
The Set Freeze Checkbox dialog pops up. Press "Don’t Freeze" to allow changes in the
Derived Column to cascade changes in the ColumnPool column if they are made.
This means that the CUS:CustNumber column is now derived from the POOL:CustNumber
column. The term "derived from" means that the POOL:CustNumber column’s definition is
the "parent" and all "children" columns which are "derived from" that column automatically
share all the attributes of the parent.
Deriving column definitions from existing columns gives you the ability to make changes in
only one place, then cascade those changes to all derived columns. For example, if the
definition of the CustNumber column needs to change in all tables using it, simply make one
change to the POOL:CustNumber column definition, then cascade that change to all the
derived CustNumber columns in all tables.

Go to the Order table, click on "CustNumber", click on the ellipsis next to the empty "Derived
From" field. This will allow you to choose any field, so go to the "ColumnPool" and choose
"CustNumber". Click "Select", and "Don't Freeze".

Return to the "ColumnPool" and click the "Add" button. Fill in the details for OrderNumber
as shown. These are a bit different from the original "Order" table, so pay close attention.
(Alternatively, you could use the ellipsis button to "import" the details from the "Order"
table's "OrderNumber" column, and then remove the "Derived from" information and change
the Data Type to "LONG" and the Screen Picture to "@n_6".) Click "OK".
Fill in the details for "ProdNumber", which we will use as the PK in the "Product" table, as
well as part of the "Detail" table. Click "OK". You should get an empty "Column Properties"
dialog box. We don't need to add any more columns to the pool, so click "Cancel".

Go to the "Order" table and click on the "OrderNumber" column. Note how its current Data
Type is "SHORT" and its Screen Picture is "@n-7". Click in the "Derived From" field and type
in "POOL:OrderNumber" and press the Tab key. Choose "Don't Freeze". Notice how the Data
Type and Screen Picture have both changed to the properties of POOL:OrderNumber. Click
on the "Save Current File" button (Ctrl-S).
The Phone Table

Click on the "Phone" table, then on the "Columns" folder. Click the "Add" button and then the
ellipsis button to the right of the "Derived From" field. Choose the "CustNumber" field in
the column pool and click "Select", "Don't Freeze", "OK".

The next field is "PhoneType" and is a string field of 6 characters. It will contain words like
"Home", "Work", "Fax", "Cell", etc.
In the "Attributes" tab, set the "Initial Value" to 'Home' (including the single quotes) to create
a default value.

Go to the "Validity Checks" tab, click on "Must be in List" and type in the following in both
the "Values" and "Choices" fields: "Home|Work|Fax|Cell|Mobile|Other". Each choice is
separated by a vertical bar, and the user will select from a list.
When you specify a "Must be in List" option, the default window control for the column is an
OPTION structure with RADIO buttons. These appear by default in the Window Controls list.
In the "Controls" tab, change the "Control Type" to "LIST". Click "OK".

The next field is "OutsideUSA". Change the "Data Type" to "BYTE".


Set the "Initial Value" to 0 in the "Attributes" tab.

Choose "Must be True or False" in the "Validity Checks" tab. Set the True Value to 1 and the
False Value to 0. The default control is a checkbox. Click "OK".
Type "PhoneNumber" in the "Column Name" entry. The "Data Type" is "STRING" of 20
Characters (to allow for the occasional international number). Type in "@P(###)###-
####P" (without the quotes) in the "Screen Picture" field. This will set a "mask" for domestic
US phone numbers. We’ll use the OutsideUSA column in our application to change the
picture later to accommodate international phone numbers. Click "OK".

The last column for this table is "Extension", and it is 10 characters long. Click "OK", then
"Cancel". Remember to save your work.
The Detail Table

Go to the "Detail" table, click on "Columns", click the "Add" button, then the ellipsis on the
right of the "Derived From" field, and select "OrderNumber" from the column pool and
click on "Select", "Don't Freeze", "OK".

Click on the ellipsis button and choose "ProdNumber" this time. Click "Select", "Don't
Freeze", "OK".
Create the column "Quantity" as a "SHORT" data type (Two byte signed integer, with a
range from -32,768 to 32,767) and click "OK".

Create the column "ProdAmount" as a "DECIMAL" data type with 5 characters and 2
decimal places. Click "OK".
Create the column "TaxRate" as a "DECIMAL" data type with 2 characters and 2 decimal
places. Click "OK", then "Cancel". Save your work.

The Product Table

Go to the "Product" table, click on "Columns", click the "Add" button, then the ellipsis on the
right of the "Derived From" field, and select "ProdNumber" from the column pool and click
on "Select", "Don't Freeze", "OK".
Create the column "ProdDesc" as a "STRING" data type with 30 characters. Click "OK".

Create the column "ProdAmount" as a "DECIMAL" data type with 5 characters and 2
decimal places. Click "OK".
Create the column "TaxRate" as a "DECIMAL" data type with 2 characters and 2 decimal
places. Click "OK", then "Cancel". Save your work (Ctrl-S).
Make a backup of your work.

OK, What Did I Just Do?


Here’s a quick recap of what you just accomplished:
• You created new table definitions.
• You created a pool of column definitions from which new column definitions
can be easily derived.
• You created the column definitions for all the tables.
Next we’ll go on to add keys and table relationships.

Lesson 4: Adding Keys and Relations


Now that all the tables are defined, we can add keys then specify the table relationships. You
already have defined the keys for the two tables you created in the GSLesson application in
the Getting Started lessons. In this chapter, we’ll define keys for the remaining tables.

The columns in the "Order" table that relate to other tables in the database are the
"OrderNumber" and "CustNumber" columns. The "CustNumber" column relates to the
"Customer" table (purple line above). There will be duplicate values in the CustNumber
column of the "Order" table that relate to rows in the "Customer" table. The key we defined in
the "Order" table is a foreign key. The "Customer" table key does not allow duplicates and
nulls, and was defined as the primary key for that table.
Multiple "Order" rows can exist for each "Customer", making this a Many-to-One
relationship: the "Order" table is the child of the "Customer" table. The "Order" table was
imported from the Getting Started lessons, and there are two keys already defined that satisfy
the above requirements. They are both called "KEYCUSTNUMBER"

Now let's turn to the relationship shown in red in the first graphic. The "OrderNumber"
column is also the primary key (PK) of the "Order" table. Each Order row is uniquely
numbered by the "OrderNumber" field, irrespective of which customer placed the order.
Similarly, every row in the "Detail" table must belong to one and only one "Order". So we can
say that the "OrderNumber" column relates to the "Detail" table. There should be no
duplicate or null order numbers in the "Order" table; this is a primary key.
There may be multiple "Detail" rows for a single matching OrderNumber. Therefore, this is a
One-to-Many relationship: the "Order" table is the parent of the "Detail" table.

Also, there is a relationship between the "Product" table and the "Detail" table (blue line).
The "ProdNumber" column relates to the "Product" table. There will be duplicate values in
the "ProdNumber" column for the rows in the "Detail" table because a product can be sold
multiple times on different orders, assuming there is sufficient stock. There may be more
than one Detail row containing a single ProdNumber. Therefore, this is another Many-to-One
relationship, with the "Detail" table the child of the "Product" table.
Defining the Keys
The "Order" table already has the "KEYORDERNUMBER" (primary) key, but we need to
define keys for the "Product" and "Detail" tables. Let's start with the "Product" table.

Select the "Product" table, click on "Keys", and then the "Add" button. The key label is
"KEYPRODNUMBER", and it must be unique, and it is the primary key, as shown. Then click
on the "Add Field" button and choose "ProdNumber". Click on "Select", "Cancel", "OK",
and "Cancel". We now have a primary key for "Product".

Select the "Detail" table, click on "Keys", and then the "Add" button. The key label is
"KEYORDERNUMBER", and its description is "Order Number", as shown. Then click on the
"Add Field" button and choose "OrderNumber". Click on "Select", "Cancel", "OK".
The second key is "KEYPRODNUMBER" and its description is "Product Number". Click on
the "Add Field" button to choose "ProdNumber". Click "Select", "Cancel", "OK", and "Cancel".

Select the "Phone" table, click on "Keys", and then the "Add" button. The key label is
"KEYCUSTNUMBER", and its description is "Customer Number", as shown. Then click on
the "Add Field" button and choose "CustNumber". Click on "Select", "Cancel", "OK", and
"Cancel".

Defining the Relationships


The relationships for the "Customer", "Order", and "State" tables were defined in the Getting
Started exercises 3 and 5. Now we are going to turn our attention to the "Phone" and "Detail"
tables.
Select the "Phone" table, click on "Relations" folder, then the "Add" button to add a new
relationship. Since the "Phone" table is the child table in its relationship with
the parent "Customer" table, select "MANY:1" because we are looking at it from the "Phone"
table point of view.
Select "KEYCUSTNUMBER" as the foreign key. Select "Customer" from the drop-down list of
parent tables, and choose "KEYCUSTNUMBER" since it is the parent's primary key. Click on
"Map by Name" to get the key fields to match up, as shown. For "On Delete", choose
"Cascade". That means that if you delete a Customer row, all the related Phone rows will be
deleted as well. After all, you shouldn't have Phone rows for a non-existent Customer. Click
"OK", then "Cancel", since there are no other relationships to define for this table.

The "Detail" table is a child of both the "Product" table and the "Order" table. Select the
"Detail" table, click on "Relations" folder, then the "Add" button to add a new relationship.
Select "MANY:1" and choose "KEYPRODNUMBER" as the foreign key. Choose "Product" as
the parent table, and "KEYPRODNUMBER" as the primary key. Choose "Map by Name" and
"Restrict" for the "On Delete" constraint. You can't delete a product that has already been
ordered. Choose "Restrict" for the "On Update" constraint. You can't change a product
number for a product that is already ordered. Click "OK".
The "Detail" table is also the child of the "Order" table, so select "MANY:1" and
"KEYORDERNUMBER" as the foreign key, and "Order" as the parent table, and
"KEYORDERNUMBER" as the primary key. Choose "Map by Name" and "Cascade" for the
"On Delete" and "On Update" constraints. We must delete all the related Detail rows if the
Order is deleted. Click "OK", "Cancel".
Save your work using the "Save" button on the main toolbar.

Defining Relationship-Dependent Validity Checks


Now that all the table relationships are defined, we can set the Validity Checks for three
columns that we expect to use on update forms.
• When entering a new "Order" row, we can specify that the "CustNumber" must
match an existing row in the "Customer" table. You can't create an order for a non-
existent customer.
• When entering a new "Detail" row, we can specify that the "OrderNumber"
must match an existing row in the "Order" table. You can't add a detail row to an
order that doesn't exist.
• When entering a new "Detail" row, we can specify that the "ProdNumber"
must match an existing row in the "Product" table. You can't order a product that
doesn't exist.

Select the "CustNumber" column in the "Order" table. Click on the "Validity Checks" tab and
select the "Must be in Table" option. The IDE has selected the "Customer" table from the
drop-down box for "Table Label". Click "Save".
Select the "OrderNumber" column in the "Detail" table. Click on the "Validity Checks" tab
and select the "Must be in Table" option. The IDE has selected the "Order" table from the
drop-down box for "Table Label".

Click on the "ProdNumber" column and agree to save the changes. Click on the "Validity
Checks" tab and select the "Must be in Table" option. The IDE has selected the "Product"
table from the drop-down box for "Table Label". Click "Save".

Two Extra Keys


Click on the "Product" table and notice that there is only a primary key. We also need to be
able to sort the records by product description. Click on "Keys" and then on the "Add" button.
The key label is "KEYPRODDESC" and its description is "Product Description". Click on the
"Add" button and select the "ProdDesc" field. Click "Select", "Cancel", "OK", "Cancel". The
new key will show in the list of Product Keys.

Now do the same for the StateName field in the State table, as shown.
Save all your work by using the green "Save and Close" button on the DCT Explorer toolbar.
Make a backup of your work.

OK, What Did I Just Do?


Here’s a quick recap of what you just accomplished:
• You created the keys for all the new table definitions.
• You defined the relationships between the new tables.
• You defined three relationship-dependent validity checks to require that
foreign key column values always have related primary key rows in a parent table.
The data dictionary is now complete at this point. More lessons to follow.
Learning Clarion (Part 3)

This follows on from Learning Clarion (Part 2). Now that we have finished defining the data
dictionary, we will import some data into the tables.

Lesson 5: Importing Existing Data


You may have existing data from legacy applications that you want to save and use in
your Clarion application. So this chapter shows you:
• How to import a table definition from an existing table.
• How to browse and edit a table using the Database Manager.
• How to convert data from one table format to another.
First, we need to open the LCLesson.dct dictionary file.

Importing a .CSV File Definition


One easy way to convert tables is to export your old data from the previous application to
Comma Separated Values (.CSV) files. This is the file format originally used by the Basic
language. The data is contained in DOUBLE-quotes, commas separate fields, and a Carriage
Return/Line Feed separates records. Clarion’s BASIC file driver will easily read from and
write to these .CSV files.
We will import the definition of an existing .CSV file containing Customer data, then
generate a simple table conversion program (to show you just how easy it is to do) to place
the data into a TopSpeed (.TPS) table.
In the "DCT Explorer", press the "Add" button drop list, and select the "Import Table" option.

In the "Select Server" entry, choose "Comma Delimited Files (Basic)" from the dropdown list.
Press the ellipsis button to the right of the "Select Dictionary" entry to open the "Select
Database" dialog. Alternatively, you can also press the "Next" button.

Select
"C:\Users\Public\Documents\SoftVelocity\Clarion11\Lessons\LearningClarion\IMPORT1.C
SV" then press the "Open" button.
In the "Driver Options" field, press the ellipsis button to open the "BASIC Driver String
Builder" dialog. Locate and check the "First Record is Header" check box. Press the "OK"
button to close the "BASIC Driver String Builder" dialog.

Once the wizard has finished, notice the "IMPORT1" table definition has been added to the
dictionary.
View the imported data

Right-Click on the IMPORT1 table, select "Browse Table" to call the Database Browser. Or
press the "Browse" button on the DCT Explorer toolbar (highlighted in blue above).

The Clarion Database Browser allows you to directly edit the data in your tables. This is a
programmer’s tool, designed to allow you to do whatever is necessary to change the actual
data contained in your tables. This means that there are no safeguards against violating your
database’s Referential Integrity or Data Integrity rules. Therefore, you must take care when
you use this tool.
In this lesson, we do not need to make any changes at this time, and simply wanted to
identify this IDE tool if needed. Close the Database Browser at this time and return to the
DCT Explorer in the already opened GSLesson dictionary.
The "Customer" data file doesn't exist yet.
Right-click on the "Customer" table name and select "Browse Table".

Answer "Yes" to create the file.

Close the empty table once it opens.

Generate a table conversion program


In the DCT Explorer, right-click on the "Customer" table, and select the following conversion
option:
You can specify a table to convert from three different sources. The target can be directly on
disk (using Data File), a table definition in the current dictionary (using Table Definition), or
a table definition in another dictionary (using Table Definition from another DCT). We have
chosen a table from our current dictionary.

Select the "IMPORT1" table as the data source and click "Select". The table that you
select first in the conversion program process is always the target. In our example, the
"Customer" table is the target, and "IMPORT1" is the source.

The "Select New Project File" dialog appears. Accept the default name (convert.cwproj), and
the default LearningClarion folder name, by pressing the Save button. Project files determine
what source file to compile, and how to build (link) it. The default extension of project files is
*.cwproj.
Select the target table file (Customer.TPS) and click "Open".

Another dialog pops up that asks you if you would like to load the data conversion program.
Press the "Yes" button. The wizard completes, but the conversion project is not opened (No
idea why). We will return to the conversion program shortly.
The best reason to generate Clarion source code for the data conversion is to provide you the
opportunity to modify the code before you compile and execute it to handle any special data
conversion needs you may have. This makes the conversion process completely flexible to
handle any situation that can occur.

Delete the "IMPORT1" file definition


The only purpose this file definition served was to allow the Dictionary Editor to generate file
conversion source code for you. Therefore, we can delete it from the Data Dictionary right
now.
Highlight the "IMPORT1" table in the DCT Explorer, and press the Delete button in the DCT
Explorer toolbar. Press the "Yes" button when asked to confirm the deletion. Press the green
"Save and Close" button to exit the Data Dictionary Editor.

Load the conversion program

From the "View" menu, select "Solution Explorer". If it doesn't open, click on the tab on the
far right, shown by the red arrow. Expand the project to find the "Convert.clw" generated
code, and click on the "Open" button (or double-click on the file name).
Clarion’s Text Editor appears with the file loaded, ready to edit. The Dictionary Editor
created the conversion program code in this file. This contains all the Clarion language
source code necessary to read the data from the BASIC (.CSV) file and copy it to the
TopSpeed table.
Scroll down until you find the routine called "AssignRecord". Change the line that reads
CUS:State = Source:State
to read:
CUS:State = UPPER(Source:State)
This will make sure that all the 2-letter state codes are converted to upper case during the
transfer.
Also, comment out the two lines in the previous routine by using the exclamation point (see
above). For some reason, the generated code tries to copy the Customer file over itself, and
then deletes the original file, so the data is lost.
Click on the "Save" button on the main toolbar to save your work.

Compile/Build and run the conversion program


The Dictionary Editor generated the Convert.cwproj file for you at the same time it created
the Convert.clw file. Every Clarion program has a project that controls the options for
compiling the source code and linking to create the resulting .EXE file. For hand-coded
programs (and conversion programs generated by the Database Manager), these settings are
contained in a .cwproj file.
Click on the "Start without debugger (Builds solution)" button to compile and build
the program, and then run it.
This compiles the program, links it into an .EXE, then runs the resulting executable to
perform the file conversion. A status window appears as the program runs, letting you know
the progress of the file conversion. Since there are only a few records to convert in this case,
you probably won’t be able to read it (it’ll go by too fast).
Choose "Close" from the "File" menu, "File" to close the program source file and exit the Text
Editor. Then choose "File", "Close", "Solution" to close the conversion project.

Check it out
Now you can check the data in the new file by opening it with the Database Browser and
browsing through the records.

Choose "Browse Table" from the "Tools" menu.


In the "Select Driver used to Browse the Table" dialog window, highlight "TopSpeed (TPS)"
and press the "Select" button.

In the "Open TopSpeed File" dialog, press the ellipsis button, and select the
"CUSTOMER.TPS" file, then press the "Open" button, then click "OK".
This demonstrates another way to open the Database Browser, other than from within the
Dictionary Editor.
There are two obvious problems with this data. The first record is not data at all, but the
names of the column headings. The other problem is that we can't see the ZIP codes.

Click on the "Format Columns" button, select "CUS:ZIPCODE" from the drop-down list and
apply the "@P#####P" picture that we used in the Getting Started exercises. Click on "Apply
Formatting" and "Close". Now we can read the ZIP code data.

Notice that the data is sorted by default according to the first column. The first row
(CustNumber = 0) is meaningless. It came from the "Header" record of the CSV file, so it's
time to delete it. Click on the "Delete record" button at the bottom of the window. Then click
on the "Save" button and close the file.
Make a backup of your work.

OK, What Did I Just Do?


Here’s a quick recap of what you just accomplished:
• You imported a table definition from an existing .CSV file.
• You used Clarion’s Database Browser utility to examine the contents of the
.CSV table.
• You used Clarion’s Conversion Program utility to generate code to create a
table conversion program.
• You compiled and executed a table conversion program to import data from a
.CSV table into a TopSpeed table.
Now you’ve converted some valuable existing data to the TopSpeed table format so
your Clarion applications can use it. In the next chapter, we will begin building an
application "from scratch" using the Application Generator.

Lesson 6: Starting the Application


With the Data Dictionary complete, you now can use the Application Generator to create
your application. This chapter shows you:
• How to create the .APP file, which stores all your work for the project.
• How to define the first (Main) procedure to create an MDI application frame,
and how to call procedures from the application’s menu.
We begin the lesson with an empty Clarion IDE.

From the "File" menu, select "New", "Solution, Project or Application".

In the resulting dialog box, type in "LCLesson" for the application name, and remove the
check mark from "Auto create project subdir". Click on the "Create" button.
Type in the dictionary file name "LCLesson.dct" or use the ellipsis button to select it. Remove
the check mark from "Application Wizard" because we are going to do some steps manually
instead. Click the "OK" button.

Creating the Main Procedure


The Application Tree dialog appears. This lists all the procedures for your application in a
logical procedure call tree, which provides a visual guide to show the order by which one
procedure calls another. You previously saw it in the Getting Started exercises.
The "Main" procedure is the starting point. The lesson application will be an MDI (Multiple
Document Interface) program. Therefore, the natural starting point is to define the "Main"
procedure using the "Frame Procedure" template to create an application frame. A frame is a
type of visual program-wide starting point, providing menus, toolbars, and other options.

Click on the "Main" procedure object, and then on the "Properties" button.
Click on the "Defaults" tab, and choose the "Default MDI Frame" template. Click "Select".

The Procedure Properties dialog appears. It defines the functionality and data structures for
the procedure. Press the "Actions" button.

In the "Main – Properties" dialog, type "SplashScreen" in the Splash Procedure field, then
click "OK". This names the procedure containing an opening screen that will appear for just a
brief period when the user first opens the application.

Edit the Main Window


Usually, the first task when creating a procedure is to edit the main window. You can place
controls, or if the procedure template has controls already, you can customize them.
The application frame itself never has controls. Windows doesn’t allow it. We will, however,
customize the window caption (the text that appears on its title bar). Then we will add items
to the predefined menu, which is also built into the Frame Procedure template, and create a
toolbar for the application (a toolbar can have controls).

Now click on the "Window" button to open the Window Designer.

The Window Designer appears. Here are all the tools that allow you to visually edit the
window and its controls. On the far right, you should see the "Properties" pad. Click on it (red
arrow above) to display the application properties. Find the "Title" property and click in the
field to change it to read "LCLesson Application" as shown. (If the properties pad isn't
available, you can select it from the "View" menu.)

Editing the Menu


From the Window Designer or Properties Pad, you can call the Menu Editor, which allows
you to add or edit menu items for the application frame window. As you add each menu item,
you can access the Actions dialog to name the procedure to call when the user chooses that
menu item.
For each new procedure you name for the menu to call, the Application Generator
automatically adds a "ToDo" procedure to the Application Tree. You can then define that
procedure’s functionality, just as you are now defining the application frame procedure’s
functionality.
When the Application Generator generates the source code for your application, it
automatically starts a new execution thread for each procedure you call from the main menu
(this is required for an MDI application).
In the Window Designer, click on the Menu bar and then the "Edit Menu" task at the bottom
of the properties window.

Click on the "Paste" entry because we want to insert another menu below it. Then click on the
"Add New Menu" button.

Use the "Menu Down" button to move it to the main menu level, and then change the text of
the menu to read "&Browse" as shown. This defines the text that appears on the menu to the
end user. The ampersand (&) indicates that the following character (B) is underlined and
provides keyboard access (the user can press ALT+B to drop down this menu).
Press the "New Item" button (or press INSERT). This adds a new menu item — a command
on the dropdown menu — under "&Browse".
Type "&Customers" in the "Text" field then press TAB three times.
Type "?BrowseCustomer" in the "Use" field. (This is an equate for the menu item, so code
statements can reference it. The leading question mark (?) indicates it is a field equate label.)
Next, click on the "Actions ..." button.

For the "When Pressed" setting, choose "Call a Procedure" from the drop-down list.
Type in "BrowseCustomers" in the "Procedure Name" field. This names the "ToDo"
procedure for the Application Tree. Enable the "Initiate Thread" checkbox. Click "OK". The
BrowseCustomers procedure will display an MDI "child" window, and you must always start
a new execution thread for any MDI window called directly from the application frame. The
Thread Stack field defaults to the minimum recommended value.

Add the second menu item


Press the "New Item" button. Type "&Products" in the "Text" field and press TAB three times.
Type "?BrowseProducts" in the "Use" field.
Normally, the next step is to define the action for the menu item—what happens when the
end user executes it from the menu. We’ll skip over this step for now, for this menu item only.
Later, you’ll create a procedure by copying it, then attaching it to this menu, just to show you
this capability of the Clarion environment.
Add the third menu item

Press the "New Item" button. Type "&Orders" in the "Text" field and press TAB three times.
Type "?BrowseOrders" in the "Use" field. Click on the "Actions ..." button.

In the Actions dialog, choose "Call a Procedure" from the "When Pressed" drop-down list.
Type "BrowseOrders" in the "Procedure Name" field.
Check the "Initiate Thread" box.
Press "OK" to close the ?BrowseOrders Prompts dialog.

Fixing the Menu Font

Before we leave the menus, click on the top line "MENUBAR" and change the font to "Segoe
UI"
Press the green "Save and Close" button to close the Menu Editor.
Press the green "Save and Close" button to close the Window Designer.
Press the green "Save and Close" button to close the Procedure Properties dialog.
This returns you to the Application Tree dialog. There are now three new procedures
marked as "(ToDo)": "BrowseCustomers", "BrowseOrders", and "SplashScreen". These were
the procedures you named in the Menu Editor. Choose "Save" from the "File" menu, or press
the "Save" button on the toolbar.

Creating the SplashScreen Procedure


We named a Splash procedure, and now we’ll create it.

Select the "SplashScreen" procedure, click on the "Properties" button, go to the "Defaults"
tab, choose the "Default Splash Window" template. Click on "Select".

We will use all the defaults for now. Just type in "Startup Splash Screen" in the "Description"
field. If you are like me and want to fiddle with the fonts and layout, click on the "Window"
button. Use the green "Save and Exit" buttons to return to the Application Tree. Save your
work.

Adding an Application Toolbar and Toolbar Controls


Now we return to the Main procedure to add in an application toolbar below the menu.

Click on the "Main" procedure, and then on the "Window" button to the right. This opens the
Window Designer directly.

For some inexplicable reason, every time I open the Window Designer, the "Control
Templates" pad blocks part of the screen. The quickest way to remedy this is to click on the
"Properties" pad on the right. See the red arrow above.
Open the "Toolbox" pad, and use the "pin" button to pin it in place on the left. This will move
the window over to the right, so you can see it properly.

Scroll down and drag the "TOOLBAR" control onto the frame, and let go just underneath the
menu bar. This will put the toolbar control in place. Next, we need buttons on the toolbar.

Scroll up the list of Clarion Window Controls to find the "BUTTON" control. Drag and drop a
button onto the toolbar control, as shown. Move the button to the left of the toolbar, just
below the "File" menu. Also, adjust the height of the toolbar, so the button fits nicely.
Press F4 to display the button properties. Use the delete or backspace buttons to remove
"Button1" in the "Text" property field. Do not use the spacebar to replace the text with a
space. There should be no text at all. Change the "Use" property to "?CustomerButton".

Find the "Icon" property and from the drop-down list click on "Choose File ...". The Select
Icon File dialog appears. Select "GIF Files" from the Files of type drop-down list. Select the
"CUSTOMER.GIF" file, and click on "Open".
Scroll down to find the "Tip" property and type in "Browse Customers". This adds a tool tip to
the button that will display whenever the mouse cursor hovers over the button. Find the
"Flat" property and toggle it to "True".

Scroll down to the "AT" property and expand it. Remove the check mark from the Height and
Width's "Default" property. Next, set the "Value" property of the height to 14, and for the
width set the "Value" property to 16.
At the bottom of the "Properties" pad, click on the "Actions ..." link.

For the "When pressed" action, select "Run a Procedure". Choose "BrowseCustomers" from
the drop-down list. This is the procedure name you typed for the "Browse" menu
"Customers" menu item. Pressing the button will call the same procedure. Often, a command
button on a toolbar serves as a quick way to execute a menu command. Remember to enable
"Initiate Thread" as before. Click "OK".

The second button


One way of adding a button is by drag and drop, which we did with the first button.
Another way is just click-and-click: click on the button control in the toolbox, and then click
on the toolbar where you want the button to appear. Notice that the toolbar control is de-
selected once you have created the button, to prevent another control being created when you
click somewhere else. Move the button into place next to the Customer button.

Press F4 to display the button properties. Use the delete or backspace buttons to remove
"Button2" in the "Text" property field. Do not use the spacebar to replace the text with a
space. Change the "Use" property to "?ProductButton".
Find the "Icon" property and from the drop-down list click on "Choose File ...". The Select
Icon File dialog appears. Open the "PRODUCTS.GIF" file. Find the "Tip" property and type in
"Browse Products". Toggle the "Flat" property to "True".
Normally, you attach an action to the button at this point. Skip this step for now, for this
button only. Later, we’ll copy a procedure, then call it at the point in the generated source
code which handles what to do when the end user presses the button to demonstrate
using embed points.

The third button


Drag and drop a button control onto the toolbar control.

Press F4 to display the button properties. Use the delete or backspace buttons to remove
"Button3" in the "Text" property field.
Change the "Use" property to "?OrderButton".
Find the "Icon" property and from the drop-down list click on "Choose File ...". The Select
Icon File dialog appears. Open the "ORDERS.GIF" file. Find the "Tip" property and type in
"Browse Orders". Toggle the "Flat" property to "True". Click on the "Actions ..." link.

For the "When pressed" action, select "Run a Procedure". Choose "BrowseOrders" from the
drop-down list. Remember to enable "Initiate Thread" as before. Click "OK".

Position and align the buttons


The Window Designer has a set of alignment tools that easily allow you to line up and resize
your window controls.

With the "Orders" button still selected, Ctrl-Click on the "Customers" button. This gives both
buttons "handles". With both buttons still selected, Ctrl-Click on the "Products" button.
Ctrl-Click is the "multi-select" action that allows you to perform actions on several controls
at once. Once multiple controls are selected, you can move them all by dragging on any one of
the selected controls, or you can use any of the Alignment menu’s tools on the entire group.
Now all three buttons have "handles", and the "Orders" button has the white handles that
indicate it has focus and is the "key control" for the alignment actions. Click on the "Align
Tops" button on the toolbar above, as shown. Notice how all the buttons have the same top
position as the key control. Drag the group up to the top of the toolbar control, so they fit
under the menu bar.

Click on the "Spread Horizontally" button to space the buttons evenly. Another way to do this
is to right-click on the group, select "Format", "Horizontal Spacing", "Make Equal".
If the buttons are too far apart, use "Format", "Horizontal Spacing", "Decrease" to move them
closer together. Make sure they don't overlap! We need more space to the right for what is
coming next.

Add the Frame Browse Control buttons

At the bottom left of the screen, click on "Control Templates". Then click on the toolbar
control to make sure it is selected. Choose the "FrameBrowseControl" template, then drag
and drop just to the right of the 3rd toolbar button.
Move the buttons into position so they are neatly placed.

Select the toolbar control again, and change the AT Height value to 20 so all the controls are
neatly positioned on the toolbar. Click on the green "Save and Close" button to save your
work.
Testing an Application under Development

Before you test your application, always save your work. Click on the "Save" button. Then
click on the "Start Without Debugger" button to Generate Source, Build and Run your
application.
The Application Generator generates the source code, displaying its progress in a message
window, procedure by procedure. Next, the Output window appears, showing you the
progress of the build as the compiler and linker do their work. Then your Application window
appears.

Press one of the buttons on the toolbar, or choose one of the items on the Browse menu. A
message box appears that says, "The Procedure (procedure name) has not been defined".
This capability allows you to incrementally test your application, whether you have designed
all the procedures or not. You’ll fill in their functionality, starting in the next lesson (Creating
a Browse).
Press the OK button to close the message box. Choose "Exit" from the "File" menu to close
the LCLesson application.
Throughout the rest of this lesson, feel free to Make and Run the developing application
whenever the lesson instructs you to save the file.
Look at the Generated Source Code
Let’s take a quick look at what the Application Generator has done for you. The whole
purpose of the Application Generator (and its Templates) is to write Clarion language source
code for you. There is no "magic" to what the Clarion tool set does to create applications; it
all goes back to the Clarion programming language.

With the Application Tree dialog open, change the "Tree Mode" drop list to "Module". This
changes your view of the application from the logical procedure call tree to the actual source
code modules generated for the application.
Highlight the "LCLesson.CLW" module, right-click to display the popup menu, then select
"Module Source File".

This takes you right into the Text Editor, looking at the last source code you generated (the
last time you pressed the Run button). Any changes you made since the last time you
generated code will not be visible here.
The "LCLesson.CLW" file is the main program module for this application, containing the
Global data declarations and code. Don’t be intimidated looking at all this code, we will
discuss Clarion Language code in a subsequent lesson.
When you have finished looking at the code, go to the "File" menu, choose "Close", "File" to
exit the Text Editor and return to the Application Generator (You can also simply press the
Close button "x" in the source window).

With the Application Tree dialog open, change the "Tree Mode" drop list back to "Procedure".
Right-click on the "Main (Frame)" procedure and select "Module Source File".

This takes you into the Text Editor again, looking at the last source code you generated for
the Main procedure. Again, any changes you made since the last time you generated code
will not show up in this code.
You may have noticed that right below the "Module Source File" selection was another called
"Embeditor Source". Do not confuse these two, they do very different things. We will
demonstrate the Embeditor Source selection later in the lessons.
If you do make any changes to this code, you actually can compile and run the program to see
what effect the changes make, however, your changes will be lost the next time you generate
source code. Therefore, it is not a good idea to make any changes here. Close the source
window. Save your work.
Make a backup of your work.

OK, What Did I Just Do?


Here’s a quick recap of what you just accomplished:
• You created a new .APP file, without using a wizard.
• You created an application Frame procedure, without using a wizard.
• You created a menu in your application frame.
• You created a splash screen procedure for your application.
• You created a toolbar under your application’s main menu and placed
iconized, flat buttons on it.
• You used Clarion’s resize and alignment tools to adjust your screen design.
• You used a Control Template to populate your toolbar with a set of standard
navigation buttons.
• You compiled and ran your work-in-progress to test its functionality.
In the next lesson, we’ll add a Browse procedure to the application.

Learning Clarion (Part 4)

This follows on from Learning Clarion (Part 3). First, we are going to create a browse
window, and then an update window.
These are the ones we created in the Getting Started exercise 8 and following.

Lesson 7: Creating a Browse


In this lesson, you’ll create a browse window similar to the one created for you by the
Application Wizard. The Application Generator uses the same templates, which generate the
same basic code; but doing it this way, you’ll have a chance to "do it from scratch." This
shows you just how much the Wizards do for you, and how you can do it all yourself, too. We
start with the Customer browse window, using the Browse procedure template.

Double-click on "BrowseCustomers" in the Application Tree. This opens the "Select


Procedure Type" dialog. Go to the "Defaults" tab and highlight the "Browse with Update and
Select" Procedure template. Then press the "Select" button.
The "Procedure Properties" dialog appears. Type in a description as shown and go to the
"Extensions" tab.

Click on the "Insert" button, and choose the "WindowResize" extension. Click "Select".

This Extension template generates code to automatically handle resizing and re-positioning
all the controls in the window when the user resizes the window, either by resizing the
window frame, or by pressing the Maximize/Restore buttons. Press the "OK" button to close
the window resize template dialog.
Return to the "Properties" tab of the "BrowseCustomers" procedure properties, and click on
the "Window" button.

Click on the window’s title bar, and open the Properties Pad (F4). Type "Browse Customers"
in the Title property. Change the "FrameType" property to "Resizable". It's the top property
in the list. Scroll down and find the "MaximizeBox" property and change it to "True". These
steps allow the user to resize the frame at runtime.

Populating and Formatting a List Box Control


The List Box Formatter allows you to format the data in the list.
Right-click on the list box control, and select "List Box format ..."

Click on the "Add Field" button (circled in blue above). This opens the "Select Column"
dialog. Click on "<ToDo>" and then "Add". Choose the "Customer" table and click "Select".

This adds the "Customer" table to the Table Schematic in the Select Column dialog, which
now lists the table and its columns. With the "Customer" table selected, click on the "Change"
button.

Highlight "KeyCustNumber" and press the "Select" button. This is important, because it sets
the display order for the rows in the list. If you don’t specify a key, the rows appear in (sort
of) whatever order they were added to the table (also called "Row Order").
Click on the "Company" field and then on "Select". This adds the "Company" field to the list
box formatter.

Apply special formatting to the first column

The tabs on the right allow you to format the appearance of the column highlighted in the list
on the left. In the Property list in the lower right pane, locate the "Right Border" and
"Resizeable" properties and set them to "True". This adds a resizable right vertical border to
the column at runtime.
Set the Header "Indent" property to 2 to slightly indent the heading text and verify that the
"DataIndent" property is already set at 2.

Populate more columns


Click on the "add Field" button and choose the "FirstName" field from the "Customer" table.
Click "Select" to add it to the list box. Check that the "Right Border" and "Resizeable"
properties are set to "True". Repeat this process for the "LastName" and "Address" fields.

As the list box begins to grow, you can resize the List Box Formatter as needed to view
additional elements.
Group some columns

With the "Address" field highlighted, click on the "Add Group" button. By creating a new
group, in which you’ll place the address information, you can add a group header. This
appears above the column headers, and visually links the data in the columns beneath. Notice
that, as you add columns and make changes, you can see the effects of your changes in the
sample list box at the top of the List Box Formatter dialog. Set the new Header Text to
"Address Info". This provides the text for the group header. Then click on the "+" icon to
expand the group, so you can see its fields.
Any columns appearing to the right of this one will be included in the group, until you define
another group. As you add columns, the List Box Formatter continually updates its sample
window (at the top) to show you how your list will appear.
Select the "Address" field and then click on the "Add Field" button to add the "City", "State"
and "ZIPcode" fields.

You can use the editor to resize any fields as required. I have widened the "State" column for
readability. Then click "OK" to close the List Box formatter.
Adding tabs
When the default Application Wizard created this procedure it had tab controls that changed
the list’s sort order depending on which tab was selected. Therefore, we’ll add this
functionality right now to show how easy it is to accomplish.

First, click on the window to get the "drag handles" and drag the bottom one down to make
the window bigger.

Lasso all the controls on the window and then drag them down as shown, to make space at
the top.
Display the toolbox, and drag the "SHEET" control to the top-left corner of the window.

Resize the sheet control by using the grab handles until the sheet covers the list box and all
the buttons below it. Then right-click to the right of the "Tab1" and choose "Send to Back".
Click just below "Tab1" and change the Text to "by Customer number". We will set the display
order of the list box to this order shortly. Then click on "Select SHEET" at the bottom of the
properties window.

Notice that the grab handles have changed back to the entire sheet. Click on the "Add Tab"
link, and then click below the text "Tab2" to select it.
Change the text to read "by Company Name".

Instead of clicking on "Select SHEET" at the bottom of the properties window, another way to
return to the entire sheet control is to select it by name from the drop-down list at the top of
the properties window. Try it now.
Select the third tab from the drop-down list as shown, and change the tab text to "by ZIP
code".

Hiding the Buttons


The toolbar buttons defined on the Frame procedure actually just tell hidden buttons in the
Browse procedure to do what they normally do. Therefore, when you are designing a Browse
procedure without using the Wizards, you do need to have the update buttons on the screen,
but the user does not have to see them at runtime. So let's hide them.

Click on the "Close" button. Then Shift-Click on the other buttons in the row to select them
too. Change their "Hide" property to "True".
Select the LIST control from the properties dropdown list, and use the grab handles to make
the listbox control use up more space on the window. It can cover the hidden buttons too, as
shown.

Setting the Sort Orders


Now that the tabs are there, we need to tell the list box what alternate sort orders to use and
when.

Right-click on the list box control and choose "Actions ..." or click on the "Actions ..." link at
the bottom of the properties window.
Click on "Browse Box Behaviour" and then choose the "Conditional Behaviour" tab. Click
"Insert".

Type "CHOICE(?Sheet1) = 2" in the Condition field. Then click on the ellipsis next to the "Key
to Use" field, and select "KeyCompany" from the list of keys. Click the "Select" button, then
the "OK" button.
The condition expression sets the condition under which the alternate sort order will be used.
This expression uses the Clarion language CHOICE function to detect when the user has
selected the second tab on the sheet. The generated code will use this expression in a
conditional statement that will change the sort order at runtime.
Click on the "Insert" key to create another condition. Set the "Condition" expression to
"CHOICE(?Sheet1) = 3" and select "KeyZIPcode" as the "Key to Use". Click "OK".

Now, when the user selects the second or third tab, the BrowseBox Control template will
generate code to switch to the key on the Company or ZIP column. It doesn’t need to know
what to do for the first tab because that always uses the Access Key we set in the Table
Schematic. (Customer Number). Note that you can move the conditions around as required
using the arrow buttons. Please click "OK" and "OK" to return to the Window Designer.

Click on the "Window Preview" button to see how the window will look. It still needs work,
but at least we have a starting point. It's not a full working window, just a preview. Close the
preview, and then use the green "Save and Close" button, followed by the green "Save and
Exit" button.
Finally, use the "Accept changes" (Save) button to save your work.

One Last Tweak


You may have noticed that the fonts used in the Window Preview were terrible. Let's fix that
quickly. Click on the "Properties" button.
Now click on the ellipsis to the right of the "Window" button. This opens the "Structure
Editor" that shows the code behind the window. The first line reads: "BrowseWindow
WINDOW('Browse Customers')" etc. but notice there is no font statement.

So we add in ",FONT('Segoe UI') at the end of the line, to specify the default font for the
entire window. Click on the green "Save and Close" button, followed by the green "Save and
Exit" button. Then click on the "Accept changes" (Save) button.
Make a backup of your work.

OK, What Did I Just Do?


Here’s a quick recap of what you just accomplished:
• You created a new Browse Procedure, without using a wizard.
• You added an Extension Template to automatically make the new procedure’s
window resizable.
• You used the List Box Formatter tool to design a scrolling list of rows.
• You added a Property Sheet and several Tabs to your screen design.
• You hid and moved buttons to provide a "cleaner" screen design.
• You used the Window Designer’s Preview mode to see your window design in
action.
• You set dynamic sort orders for the user based on which Tab control they
select.
Now that the first Browse procedure is complete, we’ll go on and create its associated Update
Form procedure.

Lesson 8: Creating an Update Form


In the last lesson, we formatted the Customer Browse procedure’s list box and added tab
controls to change the sort order. To finish the basic procedure, we name the Update
procedure. This is the procedure that handles the action for the Insert, Change, and Delete
buttons.
Select the "BrowseCustomers" procedure and click on the "Properties" button.

We have been here before. This time, click on the "Actions" button.
Type "UpdateCustomer" in the Update Procedure field at the bottom of the Procedure
Properties dialog. This names the procedure to update the rows displayed in the browse.
Click "OK". Then "Save and Exit". The new procedure appears in the Application Tree as a
"ToDo".

Notice that you didn’t have to start a new execution thread for the update procedure. You
want it to run on the same thread as the browse, so that the end user can’t open a form
window to change a row, then activate the browse window again, and open another form on
the same row. In other words, you don’t want a user trying to change the same row twice at
the same time.

Creating the Update Form Procedure


The Update Procedure should use the Form Procedure template to create a procedure that
the end user can use to maintain a row. It should provide a prompt and entry control for each
column in the row.
Double-click on the "UpdateCustomer" procedure, change to "Defaults", choose the "FORM
(Add/Edit/Delete)" template, and click "Select".

Type in "Update Customer" in the description field, and click on "Actions".


Notice that this dialog looks different from the Splash, Frame, or Browse Procedure
Properties dialogs because the prompts vary for each type of Procedure template. The User’s
Guide and on-line help describe the customization options available on each Procedure
Properties dialog. We don't need to change any of the defaults, so click on "OK".

Find the "Data / Tables" (F12) panel in the bottom-left corner of the screen, and double-click
on the "<ToDo>" section of "Update Record on Disk" (or use the "Add" button). Click on the
"Customer" table and click "Select".
Make the Window Resizable

In the Procedure Properties dialog, select the "Extensions" tab. Click "Insert" and choose the
"WindowResize" extensions template. Click "Select".

We don't need to change any of these defaults, so click "OK". Then click on the green "Save
and Exit" button to return to the main application tree. Now click on the "Window" button to
load the Window Designer.
Go to the "Properties" window (F4) and change the "FrameType" property to "Resizable".
Find the "MaximizeBox" property in the "Options" group and set it to "True". These two
properties ensure that the user can resize the window at runtime.

Populating the Columns


The default window design contains three controls for you already. The "OK" button will
close the dialog, accepting the end user’s input, and writes the Customer table row to disk.
The "Cancel" button closes the form without updating. The string control provides an action
message to inform the end user what action they are taking on the row. Now we need to add
the table columns to the form, so we can add or edit the data in a given row. Placing the
columns in a window is called populating it.

From the main menu, choose "Window Designer", "Populate", "Multiple Columns".

When the "Select Column" dialog appears, choose the "Customers" table, "CustNumber" row,
and "Select" button. The dialog will briefly disappear, as it waits for you to decide where to
put the controls on the form. Move the cursor to the top left and click. The program places a
caption and a data control on the form. Now do the same with each of the columns, until all
the columns have a pair of controls on the form. Then click "Cancel" to remove the "Select
Column" dialog.

You should end up with something like this. Note that you can also use the "Data/Tables" pad
and drag and drop fields directly on the window. This seems a more logical way of doing
things, and the cursor works better too. Try deleting the "ZIPcode" control and dragging it
again from the "Data/Tables" pad instead.

Moving and Aligning Columns


For a professional look, we need to move these columns around and align the sides and
bottoms of all the columns in the screen. First, we move the controls to their approximate
positions on the form.
Click on the "State Code" input control, and then Shift-Drag it to the left, to move it closer to
the "State Code" caption. Then use the grab handle in the middle of the right-hand side to
make the input box a more suitable size, as shown above. Ctrl-Click on the "State Code"
caption to select it as well. Shift-Drag the collection upwards and to the right of the "City"
field.

Repeat the same steps with the "ZIP Code" controls. Note the blue "guidelines" that appear
when you are dragging the controls, to help you line them up with the other controls. This is
very useful to keep things uncluttered and professional.

Shift-drag the "City" caption closer to the "City" input field, to remove the large gap between
the caption and the control. Shift-drag the "Address" caption to the right, until its right-hand
side lines up with the right-hand side of the "City" caption below. Shift-drag the "Address"
input control so that its left-hand edge is above the left-hand edge of the "City" input control.
Then resize the address control until its right-hand edge lines up with the right-hand edge of
the "Zip Code" control, as shown.
Click on the "City" caption. This becomes the anchor. Now shift-click on all the other captions
on the left of the form, and then use the "Align Rights" button to align all the captions to the
right edge of the anchor control.

Click on the "City" input field. This is the new anchor. Shift-click on all the input fields above
it, and then use the "Align Lefts" button so that all the left-hand edges line up with the left-
hand edge of the anchor field. You have created two (invisible) "sight lines", one to the right
of the captions, and the other to the left of the input fields, that remove the clutter and make
the form easier to read, like margins on a printed page.
Click on the "Last Name" input field, and then Shift-click on the "Last Name" caption. Move
it up and to the right of the "First Name" input field. Click on just the "Last Name" input field,
and widen it until its right-hand edge lines up with the right-hand edge of the "Zip Code" and
"Address" fields. This is another sight line. Widen the "company" field to match up, too.

Select ONE input control from each line, and then use the "Spread Vertically" button to space
them out evenly between the top and bottom controls. Don't panic when it doesn't move all
the controls.
Select the "Address" input field as an anchor. Then shift-click on the "Address" caption and
click on "Align Middles" to line them up. Select the "First Name" input field as an anchor.
Shift-click on the "First Name" caption, the "Last Name" caption and the "Last Name" input
field. Use "Align Middles" to line them up with the anchor. Try the same steps with
"Company", "CustNumber" and "City" input fields (one line at a time) to make sure all the
middles are lined up.

The end result should be something like this. The form window is almost done. Before you go
any further, save your work! Click on the "Save and Close" button to return to the
Application Tree. Click on the "Accept Changes" (Save) button.

A Quick Font Tweak


From the Application Tree, select the "UpdateCustomer" procedure and choose "Properties"
instead of "Window".
From the "Procedure Properties" screen, click on the ellipsis to the right of the "Window"
button. This takes us to the Structure Editor". Add in ",FONT('Segoe UI') at the end of the
first line, like we did in the previous lesson. Click on the green "Save and Close" button and
then on the regular "Window" button to see the new font setting in action.
Control templates generate all the source code required to create and maintain a specific
type of control (or set of controls) on your window. All the entry controls we just placed on
this window are simple controls, not control templates, because they do not need any extra
code to perform their normal function. Control templates are only used when a specific
control needs extra functionality that the "bare" control itself does not provide. For example,
the OK and Cancel buttons are both control templates: the OK button’s control template
saves the row to disk, while the Cancel button’s control template has all the "cleanup" code
necessary to cancel the current operation.

Adding a BrowseBox Control Template


Now we will use the extra space and add a browse list box for the related Phone table rows.
We will use a BrowseBox Control template that displays all the rows from the Phone table
that are related to the current Customer row.

Select the "Control Templates" pad in the bottom right of the screen. Click on the
"BrowseBox" control template and drag it to the screen, just below the "City" caption, and
release it. The "Select Column" dialog box will appear. Double-click on the "<ToDo>" entry in
the "File Browsing List Box" section and select the "Phone" table.

Click on the "Change" button and choose the "KeyCustNumber" key. Click "Select".

Notice how the Phone table is sorted by the "pho:KeyCustNumber" key. Select the
"CustNumber" column on the right, and click "Select"
Adjust the size of the list box control to fit the space on the form, as shown here. I have also
widened the "Action Message" to the right of the "Cancel" button to line up with the right-
hand sight line.

Right-click on the browse box, and select "List Box format ..." from the popup menu.
The List Box Formatter now appears, ready for you to choose the rest of the columns to
display. Change the "DataJustification" property to "Center". The default for numeric values
is "Right". Also check the "Right Border" and "Resizable" properties are set to "True". This
adds a right border to the column and allows the user to resize the column width at runtime.

Click on the "Insert Field" button, and choose the "PhoneNumber" field from the Phone table
as shown. Click "Select".

Change the Header "Width" property to 62.


Use the "Insert Field" button to add the "Extension" and "Phone Type" fields, and set the
header width of each of them to 56. Click on the "CustNumber" field and set the Header
"Indent" property to 2. Click "OK" to close the List Box Formatter.

Right-click on the List Box and select "Actions ..." from the popup menu.
Click on "Browse Box Behaviour" and then find the ellipsis to the right of the "Range Limit
Field" and select "PHO:CustNumber" to specify which field we want to limit the range on.
Click "Select".

Change the "Range Limit Type" to "File Relationship" and then click on the ellipsis on the
right of the "Related File" field. Choose "Customer" in the "Select Table" dialog, and click
"Select". This identifies the Customer table as the related table. These steps limit the rows
displayed in the list box to only those rows related to the currently displayed Customer table
row. Click "OK" and "OK" to return to the Window Designer. Save your work: click on the
green "Save and Close" button, then on the "Accept changes" (Save) button, and then use the
"Window" button to return to the Window Designer.
Now we need to make space for a row of buttons for the list box. Reduce the height of the list
box and make the form slightly taller. Move the existing buttons down a bit, as shown above.

In the "Control Templates" pad, search for the "BrowseUpdateButtons" template, and drag it
to the empty area below the Browse Box, as shown. Try to centre them below the list box, in a
way that doesn't associate them with the "OK" and "Cancel" buttons below. We don't want to
confuse the user. Save your work: click on the green "Save and Close" button, then on the
"Accept changes" (Save) button, and then use the "Window" button to return to the Window
Designer.
The Insert, Change, and Delete buttons are the buttons that will allow the toolbar buttons
to function, so they must be present in the window design. They do not have to be visible to
the end-user, so you can hide them if you choose. However, since this Browse Box is placed
on an update form procedure, for this application we’ll leave this set of
BrowseUpdateButtons visible. This will allow the user to use either set of buttons. The
toolbar update buttons will only function for this list when the list box has focus (not when
the user is inputting data into any other control) so keeping these buttons visible will ensure
that the user can easily maintain the Phone table rows.

Specify "Edit in Place" for Phone Update

Right-click on the "Delete" button and select "Actions..." from the popup menu.

Check the "Use Edit in Place" box. Click "OK". Save your work: click on the green "Save
and Close" button, then the green "Save and Exit" button, and finally the "Accept changes"
(Save) button in the Application Tree window to save your work.
Setting the Actions for one button sets them for all three buttons in the set, because they all
belong to the same control template. Since the "Phone" table is a small table with just a
couple of columns, there’s no need for a separate update procedure.
Make a backup of your work.

OK, What Did I Just Do?


Here’s a quick recap of what you just accomplished:
• You created a new Form Procedure, without using a wizard.
• You learned just how quickly you can populate data entry controls onto a Form
by using the Columnbox toolbox.
• You learned to use the Window Designer’s tools to move and align controls.
• You used the List Box Formatter again and created a range limited list box.
• You learned how to implement edit-in-place for simple updates which don’t
require a Form procedure.
Now you’ve created all the procedures necessary to maintain both the "Customer" and
"Phone" tables. In the next lesson, we’ll create the procedures that will maintain the
"Product" data table.

Learning Clarion (Part 5)

This follows on from Learning Clarion (Part 4). In the lesson below, we’ll create the
procedures that will maintain the "Product" data table

Lesson 9: Copying Procedures


Now that we’ve created the Customer browse procedure, we can reuse much of that
work for the next procedure by copying the procedure, then changing its columns. In
this lesson, we’ll copy the BrowseCustomers procedure to create the BrowseProducts
procedure.
We will also use "Embed points" to write "embedded source code" to call the
BrowseProducts procedure from your application’s menu and toolbar. This will
introduce you to the numerous points at which you can add a few (or many) lines of
your own source code to add functionality to any procedure.
As you recall, when we created your "Browse", "Products" menu item, and the toolbar
button labelled "Products," we didn’t specify a procedure to call when the end user
executed them. We’ll start by creating the procedure to call. Highlight the
"BrowseCustomers" procedure in the Application Tree dialog because we want to
copy it. From the main IDE menu, click on "Application" and choose "Copy
Procedure".

Change the text in the "New Procedure" dialog box to read "BrowseProducts" and
click "OK".
Because the "UpdateCustomer" procedure is nested under the "BrowseCustomers"
procedure (the one you are copying), the "Procedure name clash dialog appears". This
offers you options on how to handle the clashing procedures. Press the "Prompt"
button. By doing this, you tell the Application Generator to let you have the
opportunity to rename all the clashing procedures, or not.

We want our new "BrowseProducts" procedure to call a procedure to update


"Product" rows, not "UpdateCustomer", so click on "Rename" so we can specify the
correct name.
Type in the new procedure name, "UpdateProduct" and click "OK".

Notice how the Application Tree now has the copied "BrowseProducts" procedure,
and its associated (empty) "UpdateProduct" procedure. They look "disconnected"
from the other procedures because no other procedure calls them (yet). We’ll do that
next.

Working with Embed Points


The Clarion templates allow you to add your own customized code to many
predefined points inside the standard code that the templates generate. It’s a very
efficient way to achieve maximum code reusability and flexibility. The point at which
your code is inserted is called an Embed Point. Embed points are available at all the
standard events for the window and each control, and many other logical positions
within the generated code.
In this lesson you add embedded source code (using a Code template that will write
the actual source for you) at the points where the end user chooses the "Browse",
"Products" menu item, and at the point where the end user presses the "Products"
button on the application’s toolbar.

Right-click on the "Main" procedure in the Application Tree. There are several ways
to access the embedded source code points within a procedure. Two of them appear
on the popup menu that you now see.
The first is the Embeds selection, which calls the Embedded Source dialog to show a
list of all the embed points within the procedure.
The second is the Embeditor Source selection, which actually generates source
code for the procedure and calls the "Embeditor" (the Text Editor in embed point edit
mode) to allow you to directly edit all the embed points within the context of
generated source. The generated source code is "greyed out" to indicate that you
cannot edit it, and every possible embed point in the procedure is identified by
comments, following which you may type your code.
There are advantages to each method of working in embed points, so we’ll cover both
methods during this lesson. First, we’ll use the Embedded Source dialog. Choose
"Embeds" from the popup menu.
The Embeds Tree dialog appears, allowing access to all the embed points in the
procedure. (You can also get here from the "Embeds" button on the Procedure
Properties window, but the popup menu is quicker.) This list is either sorted
alphabetically or in the order in which they appear in the generated source,
depending on whether you have the Sort Embeds Alphabetically box checked in
"Setup", "Application Options". Press the "Contract All" button on the toolbar. This
will make it easier to locate the specific embed point you need.
Type in "BrowseProducts" in the search bar. This is a quick way to find a particular
procedure. Or you can click on the "+" sign next to "Control Events" and scroll down
to "?BrowseProducts".

Click on the "+" sign next to "?BrowseProducts" and select "Accepted". The
"Accepted" event for this menu selection marks the point in the generated code that
executes when the user chooses the menu command. Click on the "Insert" button.
The Select embed type dialog appears to list all your options for embedding code. You
may simply Call a Procedure, write your own Clarion language Source in the Text
Editor, or use a Code template to write the source code for you. This is one advantage
to editing embed points from within the Embedded Source dialog: you can use Code
templates to write the code for you instead of writing it yourself.
Scroll down to the "Initiate Thread" template. Click on it and click "Select".

A Code template usually provides just a few prompts and instructions on its use. It
gathers the information it needs from you to write its executable code, which it then
inserts into the standard generated code produced by the Procedure template directly
into this embed point. This Code template is designed to start a new execution thread
by calling the procedure you name using the START procedure (as shown in the
window).
Choose the "BrowseProducts" procedure from the drop-down list of available
procedure names, and click "OK". This names the procedure to START when the user
chooses the menu item. This is the name of the procedure you previously copied.
Notice that there are some up and down buttons and a spin box on the right side of
the window that allow you to select a Priority. These are important. The templates
generate much of the code they write for you into these same embed points.
Sometimes, the code you want to write should execute before any template-generated
code, and sometimes it should execute after, and sometimes it should execute
somewhere between various bits of generated code.
The exact placement of your code within the embed point is determined by the
Priority number. This provides you with as much flexibility in placing your embed
code as possible. The Priority numbers themselves do not matter, but the logical
position within the generated code does, and that’s why this dialog also shows
comments which identify the embed priorities. Don’t worry, there’s more coming on
this issue later that’ll help make it clearer.

At this point, you could do the same thing to call the BrowseProducts procedure from
the Product button. However, there’s an easier way to write this code again: just Copy
and Paste it from one embed point to another. So, with the
BrowseProducts menu embed point still highlighted, click on the "Copy" button as
shown.

Search for the "ProductButton" in the search bar, or just scroll down until you find it.
Click on the "Accepted" embed point and press the "Paste" button. When the
"Procedure name clash" dialog appears to warn you that the same procedure has
already been used, click on "Same" because we want to use the same procedure. Now
click on the green "Save and Close" button.

The BrowseProducts procedure now "connects" to the Main procedure. Now you can
customize the copied procedures for the Product table, which we copied from the
BrowseCustomer procedure.

Modify the Browse


Click on the BrowseProducts procedure and notice that the description still says,
"Browse Customers".

Click on the "Properties" button and change the description to "Browse Products".
Click on the green "Save and Close" button. Notice that the description is fixed.

Now we need to change the fields that are displayed. Click on the "Window" button to
open the window designer.
Change the window Title to "Browse Products" as shown.

Click on the main List Box, and then right-click and choose "List Box format ..." to
open the list box formatter.
Notice that all the fields still refer to the Customer table. Use the "Remove" button to
remove all the fields shown.

Now click on the "Add Field" button. It will display the current table and fields, none
of which are the ones we want. Click on the "Customer" table and then use the
"Delete" button to remove it.
Click on the "<ToDo>" word that replaced the "Customer" table and then the "Add"
button. This will open a list of tables. Click on "Product" and click "Select".

Use the "Change" button to change the default sort key to "KEYPRODDESC" and
click "Select". The "Select Column" dialog now lists the correct table and columns.
Click on "ProdNumber" and then the "Select" button.
Check that the RightBorder and Resizeable properties are both "True", and change
the Header Text property to "Prod #". The width should be 24.

Use the Add Field button to select "ProdDesc". Check that the RightBorder and
Resizeable properties are both "True". The width should be 120.
Use the Add Field button to select "ProdAmount". Check that the RightBorder and
Resizeable properties are both "True". The width should be 32.

Use the Add Field button to select "TaxRate". Check that the RightBorder and
Resizeable properties are both "True". Click "OK" to close the list box formatter.
Click to the right of the "by Zip Code" tab to select the entire tab sheet. Confirm this
by looking for the phrase "SHEET (Use = '?SHEET1')" in the properties window on
the right. Then press the "Delete" key to remove the sheet.

Use the green "Save and Close" button to save your work. Then click on the "Accept
changes" button to save your work. Return to the "Browse Products" design
window.

Add an Incremental Locator


If the list of Products to display is very long, the user can do a lot of scrolling before
finding the specific Product they want. By default, all BrowseBox Control Templates
have a "Step" row locator that allows the user to press the first letter of the value in
the sort key column to get to the first row that begins with that letter.
Sometimes with large databases, however, the user needs to enter the first several
letters to get close to the row they want. An Incremental locator provides that
functionality by specifying a string control for the user to see the information they
type. As they type, the list scrolls to the first row matching the data the user entered
thus far. This works best with STRING keys, such as the Product Description key.

Use the Toolbox, and select the "STRING" control. Drag it to the top left of the list
box control.

Use the properties window to set the "IsPicture" property to "True", and use the
ellipsis to the right of the "Use" property to choose the "ProdDesc" field from the
Product table. Use the green "Save and Close" button to save your work and return to
the main window. Click on the "Accept changes" button to save your work.
From the main screen, ensure that the "BrowseProducts" procedure is still
highlighted, then click on the "Extensions" button.

In the "Extensions" tab, click on "Browse on Product()" and then click the
"properties" button. Change to the "Conditional Behaviour" tab and use the "Delete"
button to remove the two SHEET choices that applied to the Customer browse but are
no longer relevant to the Product browse. After all, we have also deleted the SHEET
control.
Change to the "Default Behaviour" tab, and click on the "Locator Behaviour" button.
Change the "Locator" option to "Incremental" and click "OK". This completes the
requirements for the Incremental Locator. The key column of the sort order (in this
case PRO:ProdDesc) is the default locator control. Click "OK", and then on the green
"Save and Close" button.

Creating the Form Procedure


When you renamed the reference to the UpdateCustomer procedure while copying
BrowseCustomers to BrowseProducts, it made the UpdateProduct procedure a
"ToDo" procedure. Therefore, we need to create a form to update the Products table.
Click on the "UpdateProduct" procedure and select "Properties". Select the "Defaults"
tab, choose "FORM (Add/Edit/Delete)" and then press the "Select" button.

In the "Data / Tables" pad, highlight the "<ToDo>" table, then press the "Add" button
(or double-click on "<ToDo>"). Choose the "Product" table and click on the "Select"
button.

Set the Description to "Update Product" and click on the ellipsis next to the
"Window" button.

Change the WINDOW text to 'Update Product' and add in ",FONT('Segoe UI')" at the
end of the first line. Click on the green "Save and Close" button, and then use the
"Window" button to enter the Window Designer.
Starting with the "ProdNumber" field, drag and drop each field from the "Data /
Tables" pad to the form, as shown.

Spend a few minutes tidying up the form layout, and adjust the form size as shown.
Click on the green "Save and Close" button, then the green "Save and Exit" button,
and finally the "Accept changes" button to save your work. The Products table
update form window is completed.
Make a backup of your work.

OK, What Did I Just Do?


Here’s a quick recap of what you just accomplished:
• You copied an existing procedure, renaming the subsequent procedures
it called.
• You used a Code Template in the Embedded Source dialog to call the
new procedure from the main menu.
• You modified the copied procedure to display from another table.
• You added an Incremental Locator to the Browse procedure.
• You created an entire Form procedure very quickly by just using the
Populate Column toolbox.
Now that you’re thoroughly familiar with Procedure Templates, we’ll go on to use
some Control and Extension Templates.

Lesson 10: Control and Extension


Templates
For the BrowseOrders procedure, you’ll create a window with two synchronized
scrolling list boxes. One will display the contents of the Orders table, and the other
will display the related rows in the Detail table. You’ll use a generic Window
procedure, and populate it using Control templates. The only reason for doing it this
way instead of starting with a Browse Procedure Template is to demonstrate another
way of building a procedure: using Control templates placed in a generic Window
(the same way the Browse Procedure template itself was created).
Control templates generate all the source code for creating and maintaining a single
control or related group of controls. In this case, placing a BrowseBox Control
template allows the Application Generator to produce the code that opens the tables
and puts the necessary data into the structures which hold the data for display in the
list box.

Creating the Procedure

Click on the "BrowseOrders" procedure and the "Properties" button. When the
"Select Procedure Type" dialog box opens, choose the "Templates" tab, and select
"Window - Generic Window Handler".
Type in "Browse Orders" in the Description field, and then click on the ellipsis next to
the "Window" button. When the "Select default declaration" dialog appears, choose
"MIDI Child Window" and click on "Select".

Change the WINDOW caption to 'Orders' and check that the FONT declaration shows
'Segoe UI'. Then click on the green "Save and Close" button. Then click on the
"Window" button to go into the normal Window Designer.
Resize the window to double its height and width. Change the "MaximizeBox"
property to "True" and check that the Frame Type is "Resizable". Open the "Control
Templates" pad.

Drag the "BrowseBox" control template to the top left corner of the window, and
drop. The "Select Column" dialog opens. Click on the "<ToDo>" entry, click the "Add"
button, choose the "Order" table and click the "Select" button.

With the "Order" table still selected, click on the "Change" button and select the
"KeyOrderNumber" key to sort the data by order number.

Choose the first field "CustNumber" and click "Select".


Right-click on the newly-created list box and choose "List Box format ..." to open the
List Box Formatter.

Use the "Insert Field" button to click on and "Select" each of the remaining fields in
the Order table.
Adjust the column widths by moving the right border on applicable fields. Set the
Header Indent to 1 for the first field. Ctrl-click on each of the fields to select them all.
Then Check that the "Resizable" and "RightBorder" properties are "True". Examine
the Header text property of each column, and abbreviate them as follows: "Cust#",
"Ord#", "Inv Amt", "Date", and "Note". Click "OK" when done.

Set the "Vertical" and "Horizontal" scrollbar properties to "True" and widen the list
box to fill most of the width of the form, leaving an equal distance between left, right
and top parts of the window. Change the Font "Size" to "8" points so that more of of
the Note field can be displayed. Use the green "Save and Close" and "Save and Exit"
buttons to save your work and return to the main screen.
Try the "Start Without Debugger" button to see how your application is progressing.
Close the application when done, and return to the Window Designer by clicking on
the "Window" button.

Adding the Browse Update Buttons Template


Next add the standard "Insert", "Change" and "Delete" buttons for the top browse list
box control. Since there are going to be two list boxes on this window, we’ll leave
these buttons visible for the user. Later we’ll add a form procedure for adding or
editing an order.

Search in the "Control Templates" pad for "browseupdatebuttons" and locate the
"Browse on Order" template. Drag it to the bottom left of the list box as shown. The
three buttons will appear.
Select just the "Delete" button, and then right-click and choose "Actions ..." from the
pop-up menu.

Type in "UpdateOrder" in the Procedure Name field and click "OK". This names the
procedure, in the same way that you named the Update procedure for the Customer
browse in its Procedure Properties dialog. Naming the Update Procedure for one
button in the Control template names it for all three.

Placing the Second Browse List Box


Next, place a list box with the contents of the Detail table. This will update
automatically when the end user changes the selection in the top list box. To do this,
you may have to make a bit more space on the form, by shortening the "Order" list
box and moving the three buttons up to the middle of the form height.
Drag another "BrowseBox" control template to the point below the "Insert" button.
Click on the new '<ToDo>" entry, click "Add" and choose the "Detail" table before you
click "Select".

Select the new "Detail" table entry, and click on "Change" to choose the
"KeyOrderNumber" sort key, and click "Select".

Click on the "Detail" table, choose the first field and click "Select". This will return us
to the Window Designer.
Adjust the shape of the new list box to fit the window, leave some space to the right
for a "Close" button which we will add later, and then right-click on it to choose the
"List Box format..." command and open the List Box Formatter.

Click on the "Add Field" button, select the "Detail" table and add in the remaining
fields as shown, using the "Select" button.
Adjust the headings and widths of each field to make them neater. Then click "OK"
when done.

Right-click on the list box again and select "Actions ..."


Click on the ellipsis to the right of the "Range Limit Field" and select "OrderNumber"
from the "Detail" table. This is autosuggested for you. Change the "Range Limit Type"
to "File Relationship" and use the ellipsis to the right of the "Related file" field to
select the "Order" table. These settings will now show only the Detail records for the
selected "Order" record in the top browse. Click "OK".

Change the font "Size" property to "8" points so that it matches the size of the first list
box, and make sure the "Horizontal" and "Vertical" scrollbar properties are set to
"True".

Adding the Close Button Control Template


Finally, you can add a "Close" button that closes the window.
Drag and drop the "CloseButton" control template in the bottom-right corner of the
window, as shown. Use the green "Save and Close" button, followed by the "Accept
changes" button, to save your work.

Make the window resizable


Select the BrowseOrders procedure, and then click on "Properties" button.

Go to the "Extensions" tab. Click on the "Insert" button. Choose the "WindowResize"
extension and click on "Select".
Instead of just accepting the default resizing strategy, we are going to change a few
things. First, enable the "Restrict Minimum Window Size" option, to prevent the
window being made smaller than the designed size. Next, click on the "Override
Control Strategies" button and click on "Insert". Choose the "?Insert" window control
and change the "Vertical Positioning Strategy" option to "Fix Bottom". Click "OK".
Repeat these steps for the "?Change", "?Delete" and "?List:2" buttons. This ensures
that the three buttons and the bottom list box remain a constant distance from the
bottom edge of the top list box.

Insert another control strategy for the "?List" control, and set the "Vertical Resize
Strategy" to "Constant Bottom Border" and click "OK". This will ensure that the top
list box changes its height as the window grows, but the bottom list box stays the
same height. Click "OK" and "OK" again to return to the "Extensions" tab. Click on
"Accept changes" to save your work.

Set up a Reset Column


In the "Extensions" tab, highlight "Browse on Detail" and press the "Properties"
button.
Click on the "Reset Fields" button, then "Insert" and use the ellipsis to "Select" the
"InvoiceAmount" field from the "Order" table. Click "OK", "OK", "OK". This specifies
that the "Detail" table’s list box should reset itself whenever the value in the
"ORD:InvoiceAmount" column changes. This ensures that any changes you make to
an existing order are reflected in this dialog when you return from changing the
order.
Click on the green "Save and Close" button, followed by the Accept changes" button
to save your work.
Make a backup of your work.

OK, What Did I Just Do?


Here’s a quick recap of what you just accomplished:
• You created a new browse procedure, but did it using the BrowseBox
and BrowseUpdateButtons Control Templates instead of the Browse
Procedure Template.
• You created a second, range-limited list box to display related child
rows.
• You used the WindowResize Extension Template and specified
individual control resize strategies.
• You set a Reset Column on the Detail table’s Browse list so its display is
always kept current.
Next, we’ll create the UpdateOrder Form procedure to create and maintain the
"Order" and "Detail" table rows.

Learning Clarion (Part 6)


This follows on from Learning Clarion (Part 5). In the lesson below, we’ll create the
procedures that will maintain the "Order" and "Detail" data tables.

Lesson 11: Advanced Topics


For the "Order Update" form, we’ll place the columns from the "Order" table on an
update form, perform an automatic lookup to the "Customer" table, add a BrowseBox
Control template to show and edit the related "Detail" items, calculate each line item
detail, then calculate the order total.

Set Up the UpdateOrder Form

Highlight the "UpdateOrder" procedure in the Application Tree dialog, then click on
the "Properties" button. In the "Defaults" tab, choose "FORM(Add/Change/Delete)",
then press the "Select" button.
Type in a suitable description ("Order Form"), and then click on the ellipsis next to
the Window button.

Set the WINDOW caption to 'Order Form' and add ",FONT('Segoe UI')" to the end of
line 1. Click the green "Save and Close" button, and then click on the "Window"
button to open the Window Designer.

Go to the "Data / Tables" pad, and double-click on the "<ToDo>" entry, highlight the
"Order" table and click on "Select".
Drag and drop the "OrderDate" and "OrderNote" fields from the "Data / Tables" pad
to the top of the form, as shown.

Change over to the "Toolbox" pad, select the "ENTRY" control and drag it to a spot
below the "OrderDate" input field. Then go to the "Properties" pad on the right, click
on the ellipsis to the right of the "Use" property, and choose "CustNumber" from the
"Order" table.
Return to the toolbox, and drag a "PROMPT" control to the left of the "CustNumber"
entry control, as shown. Click on the "Edit Value" smart tag. If you miss it, you can
find "Edit Value" in the right-click popup menu. Change the value to "&Cust
Number:" and click "Accept".

In the "Properties" pad, change the "Use" property to "?CustomerPrompt".

Add a lookup procedure call into the Customer list


The standard actions for any entry control allow you to perform data entry validation
against a row in another table, either when the control is Selected (just before the
user can enter data) or when the control is Accepted (right after the user has entered
data).
Right-click on the "CustNumber" entry control and select "Actions..." from the menu.

Click on the ellipsis to the right of the "Lookup Key" for "When the Control is
Selected". Click on the "Order" table and then the "Add" button to highlight the
related "Customer" table and "Select" it.

Click on the "Customer" table, choose the "KeyCustNumber" key and click "Select".
Now we have the Lookup Key.
Now we move to the "Lookup Field", click on the ellipsis on the right, and choose
the related "CUS:CustNumber" field from the "Customer" table. Click "Select". Now
we have the Lookup Field.

Next, we need the "Lookup Procedure". From the drop-down list choose
"BrowseCustomers". Make sure the checkbox for "Force Window Refresh when
Accepted" is checked. Click "OK".

Once again, right-click on the "CustNumber" control and choose "Embeds..." from
the popup menu. This displays a list of just the embed points associated with this one
control. This is the quickest way to get to a specific control’s embed points, and it’s
the second way you’ve seen so far to get to an embed point. There is a third method
that’s still to come.

Click on the "Selected" event and press the "Insert" button. Highlight "Source" and
click "Select". This opens the Embeditor.

Start typing "?" and the floating "Populate Column" toolbox appears. This is to assist
with speed and accuracy, so you don't introduce errors from typing mistakes. We
need to type "?ORD:CustNumber{PROP:Touched} = TRUE".

It is "standard Windows behaviour" that, if the user does not enter data into a control
and just presses tab (or CLICKs the mouse) to go on to another control, an Accepted
event does not happen. This allows users to easily tab through the window’s controls
without triggering data-entry validation code on each control. However, sometimes
you need to override this "Windows standard behaviour" to ensure the integrity of
your database. The
?ORD:CustNumber{PROP:Touched} = TRUE
statement uses the Clarion language Property Assignment syntax. By setting
"PROP:Touched" to TRUE in the Selected event for this control, an Accepted event is
always generated, whether the user has entered data or not. This forces the lookup
code generated for you into the Accepted event for this control (from the information
you entered on the Actions tab on the previous page) to execute. This ensures that the
user either enters a valid Customer number, or the Customer list pops up to allow the
user to select a Customer for the Order. Click the green "Save and Close" button when
done.

Notice how the SOURCE entry has been added in. Click on the green "Save and Exit"
button to return to the Window Designer.

Drag a "STRING" control from the Toolbox and place it to the right of the
"CustNumber" control. We are going to use this to display the Customer name as a
read-only field.
In the "Properties" pad, click on the ellipsis to the right of the "Use" property and
select the "Company" field from the "Customer" table. Then change the "IsPicture"
property to "True" and set the "Picture" value to "@s30". Spend a few minutes
adjusting the fields and aligning them, so they are neat and tidy.

Placing the Detail Table's Control Templates


The next key element in this window is a browse list box control, synchronized to the
Order Number of this form. This will show all the rows in the "Detail" table related to
the currently displayed "Order" table row.

Switch to the "Control Templates" pad and drag a "BrowseBox" template just below
the "CustNumber" controls. When the "Select Column" dialog box opens, double-click
on the "<ToDo>" entry and choose "Detail". Click on "Select".
Now select the "Detail" table and click on the "Change" button to choose the
"KeyOrderNumber" key. Click "Select". Finally, click on the "ProdNumber" field and
click "Select".

Adjust the shape of the list box and then right click to choose "List Box format ..."
Change the Header "Indent" property to "2" and the "DataJustification" setting to
"Center".
Use the "Add Field" button to select the "Quantity" field, and check that its
"DataJustification" property is "Center".
Use the "Add Field" button to select the "ProdAmount" field, and check that its
"DataJustification" property is "Center".

We want to create a new variable to display the total price for each line item (the
quantity multiplied by the unit price). Click on the "Insert Field" button, click on the
"Local Data UpdateOrder" item, and click "New".
The column name is "ItemTotal". Give it a suitable description. Change the Data Type
to "DECIMAL", with 7 characters and 2 places. Click "OK". Check that its
"DataJustification" property is "Center".

Click on the "Add Field" button, select the "Detail" table, click "Add", choose
"Product" and click "Select". This adds the Products table to the Control template’s
table schematic as a lookup table. The related row from the Products table is
automatically retrieved for you so you can display the product description in the list.
Choose the "Product" table, highlight the "ProdDesc" field, and click "Select".

Adjust the field widths, change the Header "Justification" settings to "Left", and the
Header "Indent" settings to "1", except for the first field, where it should be "2".
Adjust the Header Text as shown.
We want this list to only display the "Detail" table rows that are related to the "Order"
table row currently being edited. Therefore, we need to specify a Range Limit. Right-
click on the list box and select "Actions..." from the popup menu.

In the "Range Limit Field" use the ellipsis to specify the Detail table's
"OrderNumber". Change the "Range Limit Type" to "File Relationship" and the
"Related file" to "Order". Don't click "OK" yet because we haven't finished here yet.

Add an Order Invoice Total calculation


Now we want to calculate the order total and save it in the Orders table.
Go to the "Totaling" tab and click on the "Insert" button. The "Total Target Field" is
"InvoiceAmount" in the Order table. Use the dropdown list to set the "Total Type" to
"Sum" and then click on the "E" button to open the expression builder.

Double-click on the "ItemTotal" field to add it to the "Expression" list at the bottom.
This is the column whose contents will be used in the total calculation. So far, we’ve
only declared this column and not done anything to put any value into it, but we’ll get
to that soon. Click on the green "Save and Exit" button. Then click "OK".
Go to the "Classes" tab because we want to give the object a more useful name.
Change it to read "BrowseDET" and click "OK".

With the list box still selected, go to the "Properties" pad and set the "Horizontal" and
"Vertical" scroll bar values to "True". Click on the "Save and Close" button, and the
"Accept changes" button to save your work. Then click on the "Window" button to
return to the window designer.

Add the standard table update buttons


Click on the list box, and then find the "BrowseUpdateButtons" control template and
drag it to the bottom left of the list box.

Select just the "Delete" button, right-click and choose "Actions..." from the popup
menu.
Check the "Use Edit in Place" box. Checking this box for one button in the Control
template checks it for all three. We will be using the Edit in Place technique to update
the "Detail" table rows instead of an update Form procedure. This will allow us to
demonstrate some fairly advanced programming techniques and show just how easy
they are to perform within the Application Generator. Click "OK".

Add a "display-only" Control for the Invoice Total

From the "Toolbox" pad, drag the "STRING" control to the bottom-right corner of the
list box. Change the "IsPicture" property to "True". Click on the ellipsis to the right of
the "Use" property to select the "InvoiceAmount" field from the "Order" table. This
specifies the control will display data from a variable, not just a string constant.
Change the "Picture" property to "@n$10.2". Click on the green "Save and Close"
button to close the Window Designer and save your work.

Making it all Work


There are a couple of things we need to do to make this procedure fully functional:
add a Formula, and configure the Edit in Place.
To make the "ItemTotal" calculate the correct amount for each "Detail" row in the
browse list box, we need to add a Formula to the procedure. This will also allow the
browse totalling to correctly place the invoice total in the "ORD:InvoiceAmount"
column. Click on the "Formulas" button to open the Formulas tab.

Choose the "Format Browse" template class, and click on "Insert".


The Formula Editor design dialog appears. Type "Item Total Formula" in the
"Name" field. Select "Format Browse" for the "Class" Field if it isn't already selected.
Click on the ellipsis to the right of the "Result" field. Choose "Local Data
UpdateOrder" and then "ItemTotal". This names the column that will receive
the result of the calculation. This is the column we defined earlier through the List
Box Formatter. Click "Select".

Click on the "Data" button on the right under "Operands". Choose the "Detail" table
and the "Quantity" field. Click "Select".
This places "DET:Quantity" into the "Statement" field for you. It contains the
expression being built. You can type directly into the "Statement" field to build the
expression, if you wish. Click the "*" button in the Operators group. This is the
multiplication operator. Notice how it has added the "*" to the Statement field. Click
on the "Data" button again, and select the "ProdAmount" from the "Detail" table.
Click the "Select" button.

Use the "Check" button to check the syntax of the Statement line. A green checkmark
appears left of the button, indicating the syntax is correct. If a red X appears, the
expression’s syntax is incorrect and the highlighted portion of the statement is what
you must change. Click on the green "OK" button.

Configuring Edit in Place


Now we need to configure the Edit in Place characteristics. We previously used Edit
in Place for the "Phone" table and simply took all the default behaviours because that
was a fairly simple table. However, now we’re editing a line item "Detail" row for an
order entry system, which means we need to do some data entry validation beyond
simply ensuring the user types in a number that fits the display picture. To do this,
we’ll need to extend the simple Edit in Place functionality provided by the
Application Builder Class (ABC) Library.

Change to the "Extensions" tab, select "Update a Record from Browse Box on Detail"
and click on the "Properties" button.

Click on the "Configure Edit in place" button. Double-click on the "DET:Quantity"


field, and change to the "Class" tab. The ABC Library’s EditEntryClass defaults to
using an ENTRY control, and for this column we want to use a SPIN control, so the
user can just spin to the quantity they want to order. Therefore, we need to override
some methods for this column too, to have a SPIN instead of an ENTRY control.
Clear the "Use Default ABC" check box. Change the "Base Class" to "EditSpinClass".
Click "OK".
Double-click on "DET:ProdAmount" and clear the "Allow Edit-In-Place" check box.
For this procedure we do NOT want the user to be able to edit the
"DTL:ProdAmount" column because we’re going to get its value directly from the
"Product" table, and we don’t want the user to be able to change it. That’s why we
turned off the "Allow Edit-in-Place" box. Click "OK".

Repeat these steps for the "ItemTotal" and "PRO:ProdDesc" fields. Their "Edit-In-
Place" facility should be disabled, as shown. Click "OK", "OK", "Save and Exit", and
"Accept changes" to save your work.

Using the Embeditor


Highlight the "UpdateOrder" procedure and click on the "Embeditor" button on the
right.

This opens the Embeditor: the third method of accessing embed points in a
procedure. The Embeditor is the same Text Editor you’ve already used, but opened in
a special mode which allows you to not only to edit all the embed points in your
procedure, but to edit them within the context of template-generated code.
Notice that most of the code is on a gray background and the points where you can
write your code have a white background. There are also identifying comments for
each embed point. You can turn these comments on and off as you choose through
the "Tools" -> "Options" dialog. Once you become familiar with them, you’ll probably
want to turn them off, so you can see more of the actual code.
You’ll notice that a message briefly appeared that said, "Generating LCLesson." The
Embeditor displays all possible embed points for the procedure within the context of
all the possible code that may be generated for the procedure. Notice the distinction
here: Embeditor does not show you the code that will be generated, but all the code
which could be generated for you, if you chose every possible option and placed code
into every available embed point. You are not likely to ever do that. Therefore, a lot
more generated code shows up in the Embeditor than will actually be in the
generated code when you compile your application. After we finish here, we’ll go look
at the generated code to see the difference.
At the right end of the toolbar are four buttons which are essential to know when
working in the Embeditor. These are (from left to right) the "Previous Embed", "Next
Embed", "Previous Filled Embed", and "Next Filled Embed" buttons (hover your
mouse over them and the tooltips appear naming the buttons). They allow you to
quickly get from one embed point to another; particularly after you’ve written code
into some of them.

One of the things we want this procedure to do is to detect changes to existing orders
and make sure the changes do not result in a data mismatch between the "Order" and
"Detail" tables. This system is storing the total dollar amount of an order in the
"ORD:InvoiceAmount" column, so when the user changes a "Detail" item in an
existing Order, we want to make sure the "Orders" table row is updated, too. There’s a
fairly simple way to do that which will allow us to demonstrate the ABC Library’s
flexible error handling.
Click on the "Next Embed" button (about 4 times) until you get to the embed
point immediately preceding the line of code reading
ThisWindow CLASS(WindowManager)
Each embed point potentially has 10,000 priority levels within it. This Embed code
Priority level system is designed to allow you to embed your code before or after any
generated code: whether that code is generated for you by Clarion’s ABC Templates
or any third-party templates you choose to use. This makes the embed system
completely flexible, allowing you to add your own code at any logical point needed;
before or after almost any "chunk" of generated code.
In the white area just above this line, type in (or copy and paste) the following:
LocalErrGroup GROUP
USHORT(1)
USHORT(99)
BYTE(Level:Notify)
PSTRING('Save the Order!')
PSTRING('Some Item changed -- Press the OK button.')
END
SaveTotal LIKE(ORD:InvoiceAmount)
as shown in the image above. The red text indicates that the text begins in Column 1,
and identifies a data label.
Clarion’s ABC (Application Builder Class) Templates generate Object Oriented code
for you using the ABC Library. The ABC Library contains an error handling class
called ErrorClass. This bit of code declares a LocalErrGroup GROUP (in exactly the
form that the ErrorClass requires: see the ABC Library Reference Vol I) containing a
"custom" error number and message that we are defining for use by the ErrorClass
object in our application. The "SaveTotal" declaration is a local variable which is
defined LIKE (always has the same data type) the "ORD:InvoiceAmount" column.
We’ll use this variable to hold the starting order total when the user is updating an
existing order.

Choose "Search" -> "Find" to bring up the Find dialog, or press Ctrl-F.
The text we want to find is "ThisWindow.Init", so type it in to the "Find what" field
and click the "Find" button. Click on the red "x" to close the Find dialog. You should
see "ThisWindow.Init" highlighted on the screen.

Press the "Next Embed" button (about 6 times) until you get to the point just after
SELF.Errors &= GlobalErrors
as shown above. Paste in the following:
SELF.Errors.AddErrors(LocalErrGroup) !Add custom error
IF SELF.Request = ChangeRecord !If Changing a row
SaveTotal = ORD:InvoiceAmount !Save the original order total
END
This code calls the "AddErrors" method of the "GlobalErrors" object to add the
"LocalErrGroup" to the list of available errors that the object handles. The
"GlobalErrors" object is an instance of the"ErrorClass" which the ABC Templates
declare globally to handle all error conditions in the application. Adding our
LocalErrGroup enables the GlobalErrors object to handle our "custom" error
condition. This demonstrates the flexibility of Clarion’s ABC Library.
The IF statement detects when the user is editing an existing order and saves the
original order total.
This embed point is in the "ThisWindow.Init" procedure which performs some
necessary initialization tasks. This is a virtual method of the "ThisWindow"
object. ThisWindow is the object which handles all the window and control
handling code.
You may not have noticed, but the ABC Templates generate exactly one line of
executable source code within the "UpdateOrder" procedure itself (GlobalResponse =
ThisWindow.Run) so all the functionality of the "UpdateOrder" procedure actually
occurs in object methods: either virtual methods specific to the "UpdateOrder"
procedure itself or standard ABC Library methods. This is true of every ABC
Template generated procedure. Generating fully Object-Oriented code makes the
code generated for you very tight and efficient. Only the code that actually needs to be
different for an individual procedure is handled differently. Everything else is
standard code that exists in only one place and has been tested and debugged to
ensure consistent performance.
Object-Oriented Programming (OOP) in Clarion starts with the CLASS structure. See
CLASS in the Language Reference Help for a discussion of OOP syntax.
The Advanced Programming Resources PDF contains several articles which discuss
OOP in depth, and the ABC Library Reference fully documents Clarion’s Application
Builder Class (ABC) Library.
Press Ctrl-F to get back to the Find tool, and look for "ThisWindow.Kill". Click down
two embed points and paste:
SELF.Errors.RemoveErrors(LocalErrGroup) !Remove custom error
This calls the ABC Library method to remove our "custom" error. The
ThisWindow.Kill method is a "cleanup" procedure (performs necessary exit tasks)
which executes when the user is finished working in the UpdateOrder procedure, so
the error is no longer needed at that point.

Press Ctrl-F to get back to the Find tool, and look for "EVENT:CloseWindow". Click
"Find" twice to get to the desired one. Move down to the embed point and paste:
IF SELF.Request = ChangeRecord AND | ! If Changing a row
SELF.Response <> RequestCompleted AND | ! and OK button not pressed
SaveTotal <> ORD:InvoiceAmount ! and detail recs changed
GlobalErrors.Throw(99) ! Display custom error
SELECT(?OK) ! then select the OK button
CYCLE
END
This is the code that will detect any attempt by the user to exit the UpdateOrder
procedure without saving the Orders table row after they’ve changed an existing
order. Note the vertical bar characters (|) at the end of the first two lines of code.
These are absolutely necessary. Vertical bar (|) is the Clarion language line
continuation character. This means that the first three lines of this code are a
single logical statement which evaluates three separate conditions and will only
execute the GlobalErrors.Throw(99) statement and the next 2 lines if all three
conditions are true. Click the green "Save and Close" button to save your work.

Overriding the Edit in Place Classes


OK, now you’ve seen an example of how you can use the ABC Library in your own
embedded source code. Now we’ll show you how to override a class to provide custom
functionality that the ABC Library does not provide. The CLASS declarations for the
objects that we named through the "Configure Edit in Place" dialogs are generated for
you by the ABC Templates. These CLASSes are both derived from the EditClass ABC
Library class.

With "UpdateOrder" still selected, click on the "Embeds" button to display the
Embeds Tree. Click on the "Contract All Nodes" button.
By pressing the "+" button on the tree, expand the "Local Objects", "Abc Objects",
"EIP Field Manager for Browse Using ?List for column DET:Quantity
(EditSpinClass)", "Init", "CODE", "Parent Call". Click on the "Source" button.

Go to the embed point immediately following the line of code reading


PARENT.Init(FieldNumber,Listbox,UseVar)
and paste in the following:
SELF.Feq{PROP:Text} = ListBox{PROPLIST:Picture,FieldNumber}
!Set entry picture token
SELF.Feq{PROP:RangeLow} = 1 !Set RANGE values for the SPIN
SELF.Feq{PROP:RangeHigh} = 9999
This code sets the data entry picture token and range of valid data for the SPIN
control.

Scroll to the "EditInPlace::DET:Quantity.SetAlerts" method (this is about 8 clicks of


the "Next Embed" button). In the CODE section just before the Parent Call, enter the
following code:
SELF.Feq{PROP:Alrt,5} = ''
SELF.Feq{PROP:Alrt,6} = ''
Note: If you wish to see the base class code that we’ve overridden, open the
"C:\Clarion11\LibSrc\win\ABEIP.CLW" file and search for "EditSpinClass".
Press Ctrl-F to open the find dialog and type in
EditInPlace::DET:ProdNumber.TakeEvent
in the "Find what" field. Click "Find". Close the dialog and click the "Next Embed" 3
times to get to the point below "ReturnValue = PARENT.TakeEvent(Event)". Paste in
the following code:
UPDATE(SELF.Feq) !Update Q field
IF ReturnValue AND ReturnValue <> EditAction:Cancel OR |
EVENT() = EVENT:Accepted !Check for completion
PRO:ProdNumber = BrowseDET.Q.DET:ProdNumber !Set for lookup
IF Access:Product.Fetch(PRO:KeyProdNumber) !Lookup Product row
GlobalRequest = SelectRecord !If no row, set for select
BrowseProducts ! then call Lookup proc
IF GlobalResponse <> RequestCompleted !Row selected?
CLEAR(PRO:Record) ! if not, clear the buffer
ReturnValue = EditAction:None ! and set the action to
END ! stay on same entry field
END
BrowseDET.Q.DET:ProdNumber = PRO:ProdNumber !Assign Product table
BrowseDET.Q.DET:ProdAmount = PRO:ProdAmount ! values to Browse QUEUE
BrowseDET.Q.PRO:ProdDesc = PRO:ProdDesc ! fields
DISPLAY ! and display them
END
This is the really interesting code. Notice that the first executable statement
(generated for you) is a call to the PARENT.TakeEvent method. This calls the
EditSpinClass.TakeEvent method we’re overriding so it can do what it usually does.
All the rest of the code is there to give this derived class extra functionality not
present in its parent class. This is the real power of OOP—if you want everything the
parent does, plus a bit more, you don’t have to duplicate all the code the parent
executes, you just call it. In this case, all the extra code is to perform some standard
data entry validation tasks. This code will verify whether the user typed in a valid
Product Number and if they didn’t, it will call the ViewProducts procedure to allow
them to choose from the list of products.
It also assigns the "ProdNumber", "ProdAmount" and "ProdDesc" in the local browse
with the values from the chosen record in the "Product" table. This is how
"ProdAmount" gets its correct price.

Overriding Methods in the Embeditor


There is a very important point to understand about working in the Embeditor or the
Embed Tree. When you are overriding methods: as soon as you type anything into an
embed point in an overridable method, you have overridden it. Even a simple "!"
comment line makes this happen, because the Application Generator notes that you
have written some of your own code, and so generates the proper method prototype
into the local CLASS declaration for you. To prevent you from accidentally adding a
comment that causes the method override which consequently "breaks" the
functionality, the ABC Templates automatically generate PARENT method calls and
RETURN statements for you, as appropriate.
You’ll notice that all of our overridden methods contained generated calls to their
PARENT method, and the TakeEvent method also had a generated RETURN
statement. Sometimes you want these statements to execute, and sometimes you
don’t (usually, you do). For those cases where you do not want them to execute,
simply write your code in the embed point which comes before the PARENT method
call and write your own RETURN statement at the end of your code.
This means that the generated PARENT method call will never execute. Clarion’s
optimizing compiler is smart enough to recognize that these statements can never
execute and optimizes them out of compiled object code. Click on the green "Save and
Close" button to close the Embeditor, then press "Save and Exit" to close the embed
tree. Click on the "Accept Changes" button to save your work.

Update the Procedure Call Tree

The "EditInPlace::DET:ProdNumber.TakeEvent" method calls the "BrowseProducts"


procedure from within its code. Since this is just embedded source code, the
Application Generator doesn’t know you’ve called this procedure, and needs to be
told (if you don’t, you’ll get compiler errors), so it can generate the correct MAP
structure for the module containing this procedure. Select "UpdateOrder" and click
on the "Calls" button.
Highlight "BrowseProducts" then press the green "Save and Close" button.

Notice how the "BrowseProducts" procedure has been added to the "UpdateOrder"
tree. Click on the "Accept Changes" button to save your work.

Generate Code
Click on the "Generate the currently selected Application" button in the toolbar. Then
right-click on "UpdateOrder" and choose "Module Source File" from the popup menu.

The Text Editor appears, containing the generated source code for your
UpdateOrder procedure. Notice that there is a lot less code here than there was in the
Embeditor. All that generated code in the Embeditor was there to provide you
with context, and to provide you with embed points with which to override methods,
should you need to. However, Clarion’s Application Generator and ABC Templates
are smart enough to only generate the code you actually need, when you actually need
it.
From the IDE Menu, choose "File" -> "Close" -> "File" to close the Text Editor. Now
might be a good time to try out your application. You’ve got all the data entry portions
completed and the only things lefts to do now are the reports, which we’ll get to in the
next lesson.
Make a backup of your work.

OK, What Did I Just Do?


Here’s a quick recap of what you just accomplished:
• You created a new Form procedure.
• You created a "Scrolling-Form" metaphor Edit-in-place browsebox to
update the Detail table.
• You created a total column to total up the order.
• You created a Formula to total each line item in the order.
• You used the Embeditor to write your embedded source code within the
context of template-generated code.
• You used the power of OOP to extend the standard error handling
functionality of the ABC Library.
• You used the power of OOP to derive and override the Edit-in-place
classes to extend the standard functionality of the ABC Library.
• You generated source code to compare the difference between the code
shown in the Embeditor to that which is actually generated.
We’re almost finished with this application. In the next lesson, we’ll create the
application’s reports.

Learning Clarion (Part 7)

This follows on from Learning Clarion (Part 6). In the lesson below, we’ll create the
application’s reports. But first, we need to tidy up the data.

Extra Lesson: Tidy Up the Data


Use the "Save and Exit" button to close the LCLesson app. If the "Start Page" is not
visible, click on the "Start Page" toolbar icon, and then click on "Dictionaries". Click
on the "Location" link for LCLesson, to open the folder where the files are stored:
"C:\Users\Public\Documents\SoftVelocity\Clarion11\Lessons\LearningClarion".

Find and rename the following files, changing the file extension from .tps to .tps.old:
STATE.tps
ORDER.tps
PRODUCT.tps
DETAIL.tps

Next, go to the "Solution" folder and select the following 4 files:


STATES.tps
ORDERS.tps
PRODUCTS.tps
DETAIL.tps
Once they are selected, press Ctrl-C to copy them. Go back to the "LearningClarion"
folder and press Ctrl-V to paste these files. Rename STATES to STATE, ORDERS to
ORDER, PRODUCTS to PRODUCT. It is always best to name your tables in
the singular. Close the folder and return to the Clarion "Start Page".

Click on the "LCLesson" dictionary to open LCLesson.dct. Right-click on the "State"


table and select "Browse Table" from the popup menu.

The "Convert File" dialog should appear because our index names are slightly
different to the "Solution" version. Click on the "Convert the file and backup the
existing file ..." option and click "OK".
Once the data has been displayed, click on the "x" next to "State.TPS" to close the
data file. Right-click on "Product" and select "Browse Table" and follow the same
steps. "Order" opens without conversion, but "Detail" will also need some help. Open
all the tables just to make sure. The "Phone" table is empty. The "Customer" data was
imported in Lesson 5. Click on the green "Save and Close" button. Make
a backup of your work. Change to "Solutions (Projects and Applications)" in the
"Start Page", and open the "LCLesson" application once again.

Lesson 12: Reports


The next item to cover is adding reports. First, we’ll create a simple customer list to
introduce you to the Report Designer. Then we’ll create an Invoice Report to
demonstrate how you can easily create Relational reports with multi-level group
breaks, group totals, and page formatting. Then we’ll copy the Invoice Report and
limit the copied report to print an invoice for only one customer at a time.

Updating the Main Menu


First, we need to add menu selections, so the user can call the reports, and so the
Application Generator will call the appropriate "ToDo" procedures.
Select the "Main" procedure and click on the "Window" button to the right to open
the window designer.

Right-click on the menu bar, and choose "Edit Menu" from the popup menu. This will
open the Menu Editor.
In the Menu Editor, click on the "P&rint Setup" item, and then click on the "Add new
Item (Insert)" button on the toolbar. This will add a new menu item just below the
"P&rint Setup" item.

In the "Text" property, change it to read "Print &Customer List" and press the TAB
key. Click on the "Actions..." link at the bottom of the properties pad.
Change the "When Pressed" selection to "Call a Procedure". In the "Procedure Name"
field, type in "CustReport" and enable "Initiate Thread". Click "OK".

Click on the newly created "Print &Customer List" item, and then click on the "Add
new Item (Insert)" button once more.
Type "Print &All Invoices" in the Text property entry, and press the TAB key. Click on
the "Actions..." link at the bottom of the properties pad.

Change the "When Pressed" selection to "Call a Procedure". In the "Procedure Name"
field, type in "InvoiceReport" and enable "Initiate Thread". Click "OK".

For the third menu item, set the text to "Print &One Customer’s Invoices" and set the
procedure to "CustInvoiceReport" as shown. Click "OK", "Save and Close", "Save and
Close" and "Accept changes" to save your work and return to the main procedure
display.

Creating the Report


Click on the "CustReport (ToDo)" empty procedure and then on the "Properties"
button.

Click on the "Defaults" tab and notice that you can choose 3 different page sizes for
reports. Because this program is intended for users in the USA, click on the "Report
(Paper size Letter - Portrait)" option and click on the "Select" button to continue.
Fill in "Print a list of Customers" in the Description field. Click on the "Data/Tables"
pad, and double-click on the "<ToDo>" entry. Choose the "Customer" table and click
"Select". Click on the green "Save and Exit" button and "Accept changes". Then click
on the "Report" button. We are going to use a Control Template, so it is always a good
idea to save your work before doing so.

The Report Designer appears. Here you can visually edit the report and its controls.
The Report Designer represents the four basic parts of the REPORT data structure by
showing the Page Header, Detail, Page Footer, and Form as four "bands." Each band
is a single entity that prints all together. See the "IDE User’s Guide" lesson 8 on the
Report Designer for more information on the parts of the report and how the print
engine generates them ("C:\Clarion11\docs\IDEUsersGuide.pdf").
For this report, you’ll place page number controls in the header, then place the
columns from the Customer table in the Detail band.

Click on the "Control Templates" pad, and drag the "ReportPageNumber" template to
the Page Header band as shown.
Select the STRING control in the "Toolbox" pad, and drag the control to the top left of
the Page Header band, and drop. In the Properties Pad, set the "Text" property to
"Page Number:". Tidy up the two items so they work together.

Select the "Data/Tables" pad, and double-click on the "Customer" table. Choose the
"KeyCustNumber" key and click on the "Select" button. This will cause the report to
sort the data by CustNumber.
Use the grab handles to adjust the report header size, and to make more space for the
fields in the detail band. Drag the "Company" field to the top left of the detail band, as
shown.

Drag the "FirstName" and "LastName" field to the line below "Company". Put the
"Address" field on the line below, and finish off with the "City", "State" and "ZIPcode"
fields on the last line. Drag the "CustNumber" field to the top right, and use the
familiar alignment tools to neaten everything up.
Click on the drop-down list at the top of the "Properties" pad, and select "Report".
This will allow us to set the properties of the report. Click on the ellipsis next to the
"TextFont" property, and choose a font and size. "Arial" 10pt is suggested. You could
also use a more modern font like "Cambria" or "Calibri" to match the fonts often used
in Word documents.

Before we close the report, click on the "Print Preview Details" button. Click on
"Details" and then use the "Add" button a few times to get several records to display.
Click on "OK" to see the preview, and then "Close". Click on the green "Save and
Close" button and "Accept Changes" to save your work.
Click on the "Start without Debugger (Builds Solution)" button to run the application.
Use the "File" menu and click on "Print Customer List" to preview the actual report,
and to do a printout if you want to. "Exit" the application when done. Don't leave it
running before you return to the Clarion IDE.

An Invoice Report
Next, we will create one of the most common types of reports. An invoice will make
use of most of the tables in the data dictionary, demonstrating how to create group
breaks and totals. It will also show you how to control pagination based on group
breaks.
Select the empty "InvoiceReport" procedure and then click on the "Properties"
button. Change to the "Defaults" tab, choose "Report (Paper size Letter - Portrait)"
and click on the "Select" button. Type in "Print All Invoices" in the "Description"
field.

Go to the "Data/Tables" pad and double-click on the "<ToDo>" entry. Choose the
"Customer" table and click "Select".

Double-click on the "Customer" table and select the "KeyCustNumber" key.


Make sure the "Customer" table is still highlighted, and click on the "Add" button just
above it. Select the "Order" table. With the "Order" table highlighted, click on the
"Add" button and select the "Detail" table. Check the "Inner" join option if asked.
With the "Detail" table highlighted, click on the "Add" button and select the "Product"
table.

The report will process all the Customer table rows in CustNumber order. It will
process all the Orders for each Customer. Each Order will print all the related Detail
rows. Each Detail row will lookup the related Products table row. Click on the "Accept
changes" button and then on the "Report" button. Use the "Properties" pad drop-
down to select "Report" and change the font to something suitable for your company
reports. I chose "Cambria" again.
The "Report Form" band prints once for each page in the report. Its content is only
composed once, when the report is opened. This makes it useful for constant
information that will always be on every page of the report. Go to the "Toolbox" pad
and drag a "STRING" control to the top middle of the "Report Form" band. Change
its "Text" property to "Invoice", the "Justification" property to "CENTER", "Bold" to
"True" and "Size" to 24. Use the grab handles to make the control span the full width
of the band, as shown.

Drag another "STRING" control to just below the "Invoice" control. Change its "Text"
property to your company/client name, the "Justification" property to "CENTER",
"Italic" to "True" and "Size" to 14. Use the grab handles to make the control span the
full width of the band, as shown.

Populating the Detail Band


The Detail band will print every time new information is read from the lowest level
"Child" table in the Table Schematic. For this Invoice report, the lowest level "Child"
table is the "Detail" table (remember that Product is a Many to One "lookup" table
from the "Detail" table).
From the "Data/Tables" pad, select the "Detail" table and drag the "Quantity" field to
the top-left corner of the "Detail" band.

Then drag and drop the "ProdNumber" field from the "Detail" table to the right of the
"Quantity" control.
Drag and drop the "ProdDesc" field from the "Product" table to the right of the
"ProdNumber" control.

Return to the "Detail" table and drag and drop the "ProdAmount" field to the right of
the "ProdDesc" control.
Highlight "Local Data InvoiceReport" in the "Data/Tables" pad, then press the "Add"
button. This local variable will be used to display the total price for each line item.
Type "LOC:ItemTotal" in the "Column Name" field. Change the "Data Type" to
"DECIMAL". Set the "Characters" to "7" and the "Places" to "2" and click "OK".

Drag "LOC:ItemTotal" and drop it to the right of the last control placed. Move all the
controls to the top of the Detail band, aligned horizontally, then resize the band, so it
is just a little taller than the controls. Also, modify the properties of the
"DET:ProdAmount" and "LOC:ItemTotal" controls to change the "Justification" to
"RIGHT" and the "Offset" to "2".

Adding Group Breaks


We need to print different information on the page for each Invoice. Therefore, we
need to create BREAK structures to provide the opportunity to print something every
time the "Order" table information changes and every time the "Customer" table
information changes.

Click on the "Detail" band, and then choose "Report Designer", then "Bands",
"Surrounding Break".
Use the ellipsis to the right of the "Variable" entry to choose "CUS:CustNumber", or
just type it in. Change the "Label" from "break1" to "CustNumberBreak". Then click
"OK". A "Break (CUS:CustNumber)" band appears above the Detail band. Repeat the
process to choose "ORD:OrderNumber" for the second break. Change the "Label"
from "break2" to "OrderNumberBreak".

Click on the "Break (ORD:OrderNumber)" band, and then select "Report Designer",
"Bands", "Group Header". The "Group Header(ORD:OrderNumber)" band appears
above the Detail band. This band will print every time the value in the
ORD:OrderNumber column changes, at the beginning of each new group of rows. We
will use this to print the company name, address, along with the invoice number and
date.
Click on the "Break (ORD:OrderNumber)" band, and then select "Report Designer",
"Bands", "Group Footer". The "Group Footer(ORD:OrderNumber)" band appears
below the Detail band. This band will print every time the value in the
ORD:OrderNumber column changes, at the end of each group of rows. We will use
this to print the invoice total.

Click on the "Group Footer(ORD:OrderNumber)" band, and change the "PageAfter"


property to "1" as shown. This causes the print engine to print this band, then initiate
Page Overflow. This will compose the Page Footer band, issue a form feed to the
printer, then compose the Page Header band for the next page.
Click on the "Break (ORD:CustNumber)" band, and then select "Report Designer",
"Bands", "Group Footer". The "Group Footer(ORD:CustNumber)" band appears
below the Detail and "Group Footer(ORD:OrderNumber)" bands. This band will print
every time the value in the ORD:CustNumber column changes, at the end of each
group of rows. We will use this to print invoice summary information for each
company. Click on the "Group Footer(ORD:CustNumber)" band, and change the
"PageAfter" property to "1".

Populating the Group Header Band

Click on the "Customer" table in the "Data/Tables" pad, and drag the "Company"
control to the top-left corner of the "Group Header(ORD:OrderNumber)" band. Drag
the "FirstName" and "LastName" controls to the line below, and then the "Address"
control below them. Put the "City", "State" and "ZIPcode" controls on the fourth line.
Tidy up the controls as before.

Click on the "Order" table in the "Data/Tables" pad, and drag the "OrderNumber"
control to the top-right corner of the "Group Header(ORD:OrderNumber)" band. Set
its "Bold" property to "True". Drag the "OrderDate" control to the line below it.
Change its "Justification" property to "RIGHT".

Go to the "Toolbox" pad and drag a "STRING" control to the left of the
"OrderNumber" control. Click on the "Edit Value" entry in the pop-up menu, or at the
bottom left of the "Properties" pad. Change the text value to "Order Number:" and
click "Accept".
Drag another "STRING" control below the first one, and change its text to "Order
Date:". Tidy up the controls so they look neat. Drag the bottom of the
"Group Header(ORD:OrderNumber)" downwards to make some space for a line of
text labels that will correspond to the controls in the "Detail" band.

Drag four "STRING" controls to the bottom of the


"Group Header(ORD:OrderNumber)" band, and change their text to "Quantity",
"Product", "At" and "Item Total" as shown here. Use the "sight lines" when dragging
them to the right position, and tidy them up, so they are all at the same height.
Drag a "LINE" control and drop it in some open space in the
"Group Header(ORD:OrderNumber)" band. Then move it down to below the
"Quantity" control. Move the right-hand grab handle across until it is under the right
end of the "Item Total" control.

Set the "LineWidth" property to "5" and change the "LineColor" to "ControlDark", so
we don't have a thick, ugly black line. Check that the "Height" value is 0, so we have a
straight line.

Populating the Invoice Group Footer Band


Drag a "STRING" control to the "Group Footer(ORD:OrderNumber)" band, and place
it under the "At" column. Change its "text" property to "Order Total:". Drag a second
"STRING" control and place it to the right of the first control. Set its "VariableString"
property to "True", and set the "Use" property to "LOC:ItemTotal" or use the ellipsis
to select it from the Local Values. Type "@N9.2" into the "Text" property. Change the
"TotalType" property to "Sum", and choose "OrderNumberBreak" for the "Reset"
property. This will add up all the "LOC:ItemTotal" contents for the Invoice and will
reset to zero when the value in the "ORD:OrderNumber" column changes.

Select the LINE control we created in the Invoice Header. Note the "X" value of the
line. Then use the "Edit" menu and choose "Copy". Click in the Invoice Footer band
and use the "Edit" menu to "Paste". Twice. Set the "X" values of each new line to the
same value as the original line. Make sure the "Height" values are both 0. Change the
"Y" value of the first copy to 0, and the second one to 250. The "Order Total" controls
should now have a line above and another below, equally spaced. Tidy up the controls
if necessary. Reduce the height of the Invoice footer band.

Populating the Customer Group Footer Band


Drag two "STRING" controls to the middle of the "Group Footer(CUS:CustNumber)"
band. The text for the first one should be "Invoice Summary for:" and the second one
"Total Orders:".

Go to the "Data/Tables" pad, and click on the "Local Data InvoiceReport" section.
Click on the "Add" button. Type in "LOC:InvoiceCount" in the "Column Name" entry.
Change the "Data Type" to "LONG" and the "Screen Picture" to "@N3" and click
"OK". Then click "Cancel" when the empty "Column Properties" dialog box comes up.
Drag the "LOC:InvoiceCount" control to the right of the second "STRING" control.
Check that its "VariableString" property is "True". Set the "TotalType" to "Count" and
select "CustNumberBreak" for the "Reset" property. This is the same type of total
column that we placed in the "ORD:OrderNumber" group footer, but it will only reset
when "CUS:CustNumber" changes. Select "OrderNumberBreak" for the "Tally"
property. This total column will count the number of invoices that print for each
customer. The Tally list allows you to select the point(s) at which the total
increments. By selecting "OrderNumberBreak" from the list, the count will only
increment when a new invoice begins.

From the "Toolbox" control pad, drag a "STRING" control right of the Invoice Count
string you just placed. Set the "VariableString" property to "True". Set the "Use"
property to "LOC:ItemTotal", the "TotalType" to "Sum", and the "Reset" property to
"CustNumberBreak". This is the same type of total column that we placed in the
"ORD:OrderNumber" group footer, but it will only reset when "CUS:CustNumber"
changes.
Go to the "Data/Tables" Pad. Select the "Customer" table and drag the "Company"
control just right of the "Invoice Summary for:" string you placed. Tidy up the
controls, and resize the bands to make them look neater. Click on the green "save and
Close" button and the "Save and Exit" button. Then "Accept changes" to save your
work.

Adding a Formula

Select the report once again and click on the "Properties" button.
Click on the "Formulas" tab in the Procedure Properties dialog, and the "Insert"
button to open the Formula Editor.

The Formula Editor design dialog appears. Type "Item Total Formula" in the "Name"
entry. In the "Class" entry, select "Before Print Detail" from the Template Classes list.
The "Before Print Detail" class tells the Report template to perform the calculation
each time it gets ready to print a Detail. Press the ellipsis button for the Result entry.
Highlight "Local Data InvoiceReport" in the Tables list, select "LOC:ItemTotal" from
the Columns list, then click the "Select" button.
Press the "Data" button in the Operands group. Highlight the "Detail" table in the
Tables list, select "Quantity" from the Columns list, then click on the "Select" button.

This places the "DET:Quantity" column in the "Statement" entry for you. The
Statement entry contains the expression being built, and you can also type directly
into it to build the expression, if you wish. Click on the "*" button in the "Operators"
group. Then click on the "Data" button in the "Operands" group.
Choose the "Detail" table in the Tables list, then the "ProdAmount" from the Columns
list, and click on the "Select" button.

Now you should see the complete calculation in the statement entry:
"DET:Quantity*DET:ProdAmount". Click on the "Check" button to check for syntax
errors. Click on the green "OK" button to close the Formula Editor design window.
Notice the new "Item Total Formula" has been added. Click on "Save and Exit" and
"Accept changes" to save your work.

Try running the application by clicking on the "Start without Debugger (Builds
Solution)" button. Select the "Print All Invoices" report and preview it. The first page
is displaying the invoice correctly, but on the third page there are "ghost" invoices,
with an invoice number of zero. We need to fix this.

Row Filters vs Inner Joins


One option would be to return to the report properties, and click on the "Actions"
button. Then click on the "Report Properties" button and type in "ORD:OrderNumber
<> 0" in the "Record Filter" entry. It's not the best way to fix it, but click "OK" and
"OK" and try the report now.
This will eliminate all the customers who have not ordered anything. Internally, the
Report Template generates a VIEW structure for you. This VIEW structure, by
default, performs an outer join on the tables you placed in the Table Schematic. Outer
join is a standard term in Relational Database theory: it just means that the VIEW
will retrieve all Parent table rows, whether there are any related Child table rows or
not.
If it retrieves a Parent row without a Child, the columns in the Child table are all
blank or zero, while the Parent table’s columns contain valid data. Therefore, this is
the condition for which we test.
"ORD:OrderNumber <> 0" checks to see if the ORD:OrderNumber column has any
value in it other than zero. Since "ORD:OrderNumber" is the key column in the
"Order" table that creates the link to the related Customer table row, it must contain a
value if there are existing Order table rows for the current Customer.
If ORD:OrderNumber does not contain a value other than zero, the current Customer
table row is skipped ("filtered out"). This eliminates printing Parent rows without
related Children (in this case, any Customers without Orders).
This means that a filter would work. However, since the VIEW structure can do the
filtering required, there is a better way. This is the "inner join". What this means is
that there is a Customer row only if there is a related Order row. This makes the
VIEW smaller, and thus more efficient than a filter.
Return to the "Report Properties" dialog and remove the "Record Filter" entry.
Open the "Data/Tables" pad and select the "Order" table. Click on the "Change"
button and check the "Inner" box. This changes the join type from an Outer Join to
an Inner Join which only selects related rows from "Customer" and "Order". Click
"OK".

Click on the "Window" button in the properties tab. This will open the report progress
window in the Window Designer. Click on the title bar on the window, and change the
"Text" property to "Invoice Progress...". Click on the green "Save and Close" button,
and then on the green "Save and Exit" button to return to the main Application Tree.
Click on "Accept changes" to save your work. Try running the "Print all Invoices"
report again.

A Range Limited Report


Next, we will limit the range of rows that will print.
From the Application Tree, choose the "InvoiceReport" procedure. Go to the
"Application" menu and select "Copy Procedure".

The New Procedure dialog appears. Type "CustInvoiceReport" in the entry box, then
click "OK". Click on the "properties" button for the new (copied) procedure which has
replaced the "<ToDo>" placeholder. Change the "Description" entry to "Print
Customer Invoices".
Change to the "Embeds" tab and click on the "Collapse All" button. Then click on each
of these entries to expand them: "Local Objects", "Abc Objects", "Window Manager
(ReportManager)".

Find the first "Init" procedure, click on "CODE", then "Open Files". Click on the
"Insert" button.
Choose "Source" and click "Select". This embed point is at the beginning of the
procedure, before the report has begun to process. It’s important that the tables for
the report already be open because we will call another procedure for the user to
select a "Customer" row. If the tables for the report weren’t already open, the
procedure we call would open the "Customer" table for itself then close it again and
we would lose the data that we want to have for the report. This has to do with
multithreading and the Multiple Document Interface (MDI). See "THREAD" in the
Language Reference for more on this.

Type in "GlobalRequest = SelectRecord" and click on the green "Save and Close"
button. This code sets up a Browse procedure to select a row (it enables the Browse
procedure’s "Select" button).
Make sure the SOURCE you just added is still selected. Then click on the "Insert"
button.

Choose "Call a procedure" then click the "Select" button.


Select "BrowseCustomers" in the list, then press the "OK" button. This will generate a
procedure call to the BrowseCustomers Browse procedure to allow the user to select
which Customer’s Invoices to print.
Notice that there are now two entries displayed under the embed point. At each
embed point you can place as many items as you want, mixing Code Templates with
your own SOURCE or PROCEDURE Calls. You can also move the separate items
around within the embed point using the arrow buttons, changing their logical
execution order (the first displayed is the first to execute). Note well that moving
them will change the assigned Priority option setting for the moved item if you
attempt to move a higher priority item in front of another with a lower priority
setting.

Set the Range Limit

Go to the "Properties" tab, and then press the "Actions" button. In the Properties
dialog, press the "Report Properties" button.
The Report Properties dialog appears. This dialog allows you to set either Row
Filters or Range Limits (along with Hot Columns and Detail Filters).
Row Filters and Range limits are very similar. A Row Filter is a conditional
expression to filter out unwanted rows from the report, while a Range Limit limits the
rows printed to only those matching specific key column values. They can both be
used to create reports on a subset of your tables, but a Range Limit requires a
key and a Row Filter doesn’t. This makes a Row Filter completely flexible, while
a Range Limit is very fast. You can use both capabilities if you want to limit the
range, then filter out unneeded rows from that range.
Select the "Range Limits" tab. Press the ellipsis button for the "Range Limit" entry.
The "CUS:CustNumber" control, the only logical choice, is automatically populated.
Leave "Current Value" as the "Range Limit Type". Current Value indicates that
whatever value is in the column at the time the report begins is the value on which to
limit the report. Since the user will choose a Customer row from the BrowseCustomer
procedure, the correct value will be in the CUS:CustNumber column when the report
begins. Now press the "OK" button. Press the next "OK" button, followed by the green
"Save and Exit" button.

Press the "Accept changes" button to save your work.


Click on the "Start without Debugger (Builds Solution)" button to run the application.
Use the "File" menu and click on "Print One Customer's Invoices". Double-click on
Customer 1 to preview the report for the only customer with any invoices. "Exit" the
application when done and return to the IDE. Make a backup of your work.

A Single Invoice Report


Next, we will print a single invoice from the Browse list of orders.

In the Application Tree, highlight the "CustInvoiceReport" procedure. Go to the


"Application" menu and click on "Copy Procedure". Type "SingleInvoiceReport" in
the entry box, then press the "OK" button.
Welcome to the (wild and) wonderful world of Clarion programming. If you didn't get
this message, you are fortunate. My colleagues at Clarion Hub tell me it has
something to do with using a formula. It is better to "just code it". More on that later.
For now, we will use a workaround, by deleting the formula, and then putting it back
later, after we have copied the report. Click on "Close Application".

Run the Clarion IDE and open "LCLesson" once again. Since we have been diligent
about saving our work, click on "Last Saved" to recover the application.

Select "CustInvoiceReport" and then click on the "Formulas" button.


Select the "Item Total Formula" and click on the "Delete" button, followed by "Yes".
Click on the green "Save and Exit" button.

In the Application Tree, highlight the "CustInvoiceReport" procedure. Go to the


"Application" menu and click on "Copy Procedure". Type "SingleInvoiceReport" in
the entry box, then press the "OK" button.

When we copy a procedure that calls other procedures, we have the option to call
other procedures or not. In this case, we want to continue to use the procedures that
"CustInvoiceReport" procedure is using, so click on the "Same" button.

Adding the Formula again. Twice

Select the "CustInvoiceReport" once again and click on the "Formulas" button, and
the "Insert" button to open the Formula Editor.

The Formula Editor design dialog appears. Type "Item Total Formula" in the "Name"
entry. In the "Class" entry, select "Before Print Detail" from the Template Classes list.
The "Before Print Detail" class tells the Report template to perform the calculation
each time it gets ready to print a Detail. Press the ellipsis button for the Result entry.
Highlight "Local Data CustInvoiceReport" in the Tables list, select "LOC:ItemTotal"
from the Columns list, then click the "Select" button.
Press the "Data" button in the Operands group. Highlight the "Detail" table in the
Tables list, select "Quantity" from the Columns list, then click on the "Select" button.

This places the "DET:Quantity" column in the "Statement" entry for you. The
Statement entry contains the expression being built, and you can also type directly
into it to build the expression, if you wish. Click on the "*" button in the "Operators"
group. Then click on the "Data" button in the "Operands" group.
Choose the "Detail" table in the Tables list, then the "ProdAmount" from the Columns
list, and click on the "Select" button.

Now you should see the complete calculation in the statement entry:
"DET:Quantity*DET:ProdAmount". Click on the "Check" button to check for syntax
errors. Click on the green "OK" button to close the Formula Editor design window.
Notice the new "Item Total Formula" has been added. Click on "Save and Exit" and
"Accept changes" to save your work.
Now repeat the same process to add the missing formula to the new report,
"SingleInvoiceReport".

Notice how the copied procedure appears unattached at the bottom of the application
tree. We'll "connect the lines" after we finish with the report.

A Single Invoice Report (Continued)


With "SingleInvoiceReport" selected, click on the "Properties" button and change the
description to read "Print Single Invoice" and then click on the green "Save and Exit"
button. Click on the "Embeds" button because we want to remove some embedded
code.

Click on the "Expand filled nodes" button to show the user's added code to the
generated code. The red highlights at each node will tell you that there is some
embedded code further down the tree. At the bottom, you can see two entries of
embedded code, which we want to remove.

Click on the "Show Filled Only" button, and un-click the adjacent "Show Priority
Labels" button. This will just show the stuff we want to remove. Click on the line that
reads "SOURCE (GlobalRequest = SelectRecord)" and then click on the "Delete"
button, followed by "Yes". Do the same for the next line that reads "PROCEDURE
BrowseCustomers".

Change the Table Schematic


We need to change the order of the tables in the Table Schematic. We’ll end up with
all the same tables, but instead of the "Customer" table as the primary table (first
table in the Table Schematic), we need the "Order" table to be the primary table for
the procedure, so we can easily limit the range to a single invoice.

Highlight the "SingleInvoiceReport" procedure, and select the "Data / Tables" Pad.
Click on the "Customer" table, then press the "Delete" button. The tables disappear,
and are replaced by "<ToDo>".

Click on the "<ToDo>" and then click on the "Add" button. Choose the "Order" table
from the list, and click "Select".
Next, click on the "Order" table and then the "Change" button. Select the key called
"KEYORDERNUMBER" and click on the "Select" button. So now we can use the key
to find the Order number we want to print out.

Click on the "Order" table again, and then the "Add" button. Select the "Detail" table
and click "Select".

Now repeat the same process to add the "Product" table as a related table to "Detail".

Go back to the "Order" table, click "Add" and choose the "Customer" table. Click the
"Select" button. Now we have all the related tables needed to display the data in an
invoice, and we have the correct key on the "Order" table to be able to specify exactly
which single invoice to print.
Return your attention to the "SingleInvoiceReport" procedure in the main window,
and then click on the "Properties" button.

From the "properties" tab click on the "Actions" button. Click on the "Report
Properties" button and change to the "Range Limits" tab. Use the ellipsis button to
select "ORD:OrderNumber" as the Range Limit Field, and leave the Range Limit Type
as "Current Value". This indicates that whatever value is in the column at the time the
report begins is the value on which to limit the report. Since the user will run this
report from the BrowseOrders procedure, the correct value will be in the
ORD:OrderNumber column when the report begins. Click "OK", and "OK" again.
Press the "Accept changes" button to save your work. Click on the "Report" button
because we need to make a few changes.

Select the "Break(CUS:CustNumber)" band and use the delete key to remove it. This
removes not only the Group Break, but also the Group Footer that was associated
with it. Click on the green "Save and Close" button to return to the Procedure
Properties dialog. Click on the green "Save and Exit" button in the Procedure
Properties dialog to close it. Click on the "Accept Changes" button to save your work.

Now that we have a report, we need to connect it to the rest of the application, so it
can be used. Click on the "BrowseOrders" procedure, and then on the "Window"
button.
From the "Control Templates" Pad, find the "BrowsePrintButton" collection and drag
the "Browse on Order" control to the right of the "Delete" button as shown. Right-
click on the new button and select "Properties" from the pop-up menu.

Change the Text property to "&Print Invoice". Change the "Use" property to
"?PrintInvoice". Click on the "Actions ..." link as shown.

Change the "When Pressed" property to "Call a Procedure" and then use the drop-
down list to select "SingleInvoiceReport". Click "OK".
This Control Template is specifically designed to run a range-limited report based on
the currently highlighted row in the list box we selected (Browse on Orders). The
Order table row buffer will contain the correct value to allow the Current Value Range
Limit on the "SingleInvoiceReport" to work. It also automatically adds this button's
action to the popup menu for the browse.
Click on the green "Save and Close" button, and then on the "Accept Changes" button
to save your work.
Notice how the "SingleInvoiceReport" procedure is now connected to the
"BrowseOrders" procedure. Try running the application by clicking on the "Start
without Debugger (Builds Solution)" button, or press Ctrl-F5. Then make
a backup of your work.

OK, What Did I Just Do?


Here's a quick recap of what you just accomplished:
• You added several menu items to your main menu.
• You created a simple Customer List report.
• You created a relational report to print all Invoices.
• You range-limited a report to print Invoices for a single customer.
• You range-limited a report to print a single Invoice from the current row
highlighted in a Browse list.
Now we'll look at where to go next.

What's Next?
Here is the completed application, created from scratch and without the help of the
wizards:
While this lesson application is by no means a "shrink-wrap" program, it has
demonstrated the normal process of using the Application Generator and all its
associated tools to create an application that actually performs some reasonably
sophisticated tasks. Along the way, you have used most of Clarion’s high-level tool
set, and seen just how much work can be done for you without writing source code.
You have also seen how just a little embedded source can add extra functionality to
the template-generated code, and how you can easily override the default ABC
Library classes.

A Short Resource Tour


You have many resources at your disposal to help you with
your Clarion programming. Here is a short tour of two of the more important ones
that you have right at your fingertips:
• Choose "Context Help" from the "help" menu.
This is the Contents page for Clarion’s extensive on-line Help system.
• Press the "Contents" tab in the left pane. Examine the FAQ sections.
This opens Clarion’s on-line Help file and takes you to a section of commonly
asked questions and their answers. This list of topics is the first place you
should look whenever you ask yourself any question
about Clarion programming that starts with "How do I ... ?" These topics
answer many of the most common questions that newcomers to Clarion have,
so quite often, you’ll find the answer is here.
• Examine the "Guide to Examples" section.
This topic provides jumps to the discussions of all the example programs that
come with Clarion. Here you’ll find the various tips, tricks, and techniques that
the examples demonstrate, so you can adapt them for use in your own
programs.
• Examine the "What's New?" section.
This topic always gives you the latest, up-to-date information about the most
current release of Clarion you have installed. You should always go through
this section any time you get a major upgrade or interim release. There are
generally a few last-minute details which you will find are only documented in
this section. That makes it well worth the reading.

You might also like