46
Invoking the Designer
101
TCHAR aczProjectFile [_MAX_PATH];
;
_tcscpy(aczProjectFile,"c:\\mylabel.lbl");
);
LlSelectFileDlgTitle(hJob, hWindow, "Choose label", LL_PROJECT_LABEL,
L,
aczProjectFile, _MAX_PATH, NULL);
Of course this can also be done with an individual dialog box, or a file name can be
passed to the Designer directly if the user should not be given the option of
of
choosing.
List & Label must be informed of the possible variables in order to make them
available to the user during design time or print time. Otherwise, the user could only
use fixed text in the object definitions.
First of all the variable buffer is cleared (in case variables have been previously
defined. The call is also recommended to make sure that the variable buffer is empty,
and no variables remain from the previous print job that might be meaningless in this
next task):
LlDefineVariableStart(hJob);
Now the variables can be declared in various ways. If the Designer knows sample
data for a variable, then these are used instead of the variable name in the preview
window in order to guarantee a more realistic preview display.
LlDefineVariable(hJob, "forename", "George")
LlDefineVariable(hJob, "lastname", "Smith");
And so the expression
'forename +" "+lastname'
is transformed to
'George Smith'
If list objects are also required - in a 'report' or list project (LL_PROJECT_LIST) - then
then
the programmer must also make the necessary fields available. This is done
analogously to the above (barcode fields and drawings are also possible table
columns), except that the function names contain "Field" instead of "Variable":
LlDefineFieldStart(hJob);
LlDefineField(hJob, "book");
LlDefineField(hJob, "ISBN");
LlDefineFieldExt(hJob, "ISBN", "40|15589|97531", LL_BARCODE_EAN13, NULL);
LlDefineFieldExt(hJob, "photo", "c:\\dwg\\test.bmp", LL_DRAWING, NULL);
LL);
Before calling LlDefineLayout(), menu items can be deleted by
by
LlDesignerProhibitAction() or blocked so that the Designer cannot be minimized. The
he
latter can be done by calling:
45
Programming Using the API
102
LlDesignerProhibitAction(hJob, LL_SYSCOMMAND_MINIMIZE);
Now everything has been defined sufficiently for the user to edit his project and the
he
Designer can be started:
:
LlDefineLayout(hJob, hWindow, "test title", LL_PROJECT_LABEL, test");
For a list, change the constant to LL_PROJECT_LIST, or for a file card project to LL_-
_-
PROJECT_CARD.
Please note that in the Designer you will generally see the data of only one record
(multiple times) in the Designer. It is possible to provide a real data preview in the
designer. See chapter "This chapter is only required if you're not working with one of
f
the components (.NET/VCL/OCX). If you're using one of these components, you may
skip this chapter.
Direct Print and Export From the Designer" for further information.
n.
Checking for error codes is generally recommended.
5.4
The Print Process
5.4.1
Supplying Data
List & Label also works database-independently in this mode. This means that the
he
application is (i.e. you as a programmer are) responsible for supplying the data. You
tell List & Label, by calling a function, which data (fields) are available in your
application (e.g. "A Field called <Name>, a field called <Lastname> etc.") and which
content this field should have. Where you get the data from at print time is totally
irrelevant for List & Label. In most cases you probably perform a read access to a
record field in a database.
To integrate the Designer into your application, you need to tell List & Label about the
available data fields by calling a function for each of your data fields that may be
placed in the form. With this call, you may also optionally declare the field type (e.g.
text, numeric, boolean, date etc.) to List & Label, which is e.g. relevant for the correct
treatment of your fields and variables in formulas within the Designer. You may pass
a sample content for each field, which is used during the design in the layout
preview. If you want to support the real data preview in the Designer, please follow
the instructions in chapter "This chapter is only required if you're not working with
h
one of the components (.NET/VCL/OCX). If you're using one of these components,
you may skip this chapter.
Direct Print and Export From the Designer".
".
56
The Print Process
103
During printing, the passing of data works analogously, apart from the fact that you
have to supply the real data content instead of the sample content. This has to be
done for all fields used, while you are iterating all records you want to print out.
5.4.2
Real Data Preview or Print?
nt?
In principle the printing loop always looks the same, whether you print to preview
(LL_PRINT_PREVIEW), printer (LL_PRINT_NORMAL) or file (LL_PRINT_FILE). The only
only
difference is a parameter setting at the beginning of the print (see LlPrint[WithBox]-
-
Start()). You may, however, leave the target choice to the end user
(LL_PRINT_EXPORT) by giving him the option to choose the print target within the
e
print dialog called by LlPrintOptionsDialog().
5.4.3
Basic Procedure
First of all a List & Label job is opened (LlJobOpen() or LlJobOpenLCID()) and, if
, if
necessary, global List & Label options are set (LlSetOption()). Now List & Label has to
o
be informed that printing should start (LlPrintWithBoxStart()). With this call, the
project file to be used for printing is passed to List & Label. At this point, the project
ect
is opened and parsed by List & Label. During this process a syntax check is
performed on all variables, fields and formulas used. This requires List & Label to
o
know all variables and fields you are making available to your end users. For this
reason, you must define all variables and fields using the LlDefineVariable() and
nd
LlDefineField() functions before calling LlPrintWithBoxStart().
.
As only the names and types are important at this point, not the contents, you may
call the same routine you use to define all variables and fields for the Designer (e.g.
with a sample content, or the content of the first record).
A print usually proceeds as follows (functions with '*' are optional calls which are not
necessarily required):
<open Job>
(LlJobOpen, LlJobOpenLCID)
<define List & Label-settings>*
(LlSetOption,
LlSetOptionString,
LlSetFileExtensions,
LlSetNotificationMessage,
LlSetNotificationCallback)
<print>
(see below)
<close Job>
(LlJobClose)
Printing Labels and File Cards
rds
For a label or file card print (LL_PROJECT_LABEL, LL_PROJECT_CARD), the <print>
t>
part looks as follows:
48
Programming Using the API
104
<define all possible variables>
(LlDefineVariableStart,
LlDefineVariable,
LlDefineVariableExt,
LlDefineVariableExtHandle)
<define options>*
(LlSetPrinterDefaultsDir)
<start print>
(LlPrintStart,
LlPrintWithBoxStart)
<define print options>*
(LlPrintSetOption,
LlPrintSetOptionString,
LlPreviewSetTempPath)
<let user change options>*
(LlPrintOptionsDialog,
LlPrintOptionsDialogTitle,
LlPrintSelectOffsetEx,
[LlPrinterSetup])
<define constant variables>
(LlDefineVariable,
LlDefineVariableExt,
LlDefineVariableExtHandle)
<get printer info for progress-box>*
(LlPrintGetOption,
LlPrintGetOptionString,
LlPrintGetPrinterInfo)
<skip unwanted labels>*
<print while data left and no error or user abort>
{
<give progress-status>*
(LlPrintSetBoxText,
LlPrintGetCurrentPage,
LlPrintGetOption)
<define variables>
(LlDefineVariable,
LlDefineVariableExt,
LlDefineVariableExtHandle)
<print objects>
(LlPrint)
<no warning, no user abort: next data record>
}
<end print>
(LlPrintEnd)
Printing Lists
And for printing a report (LL_PROJECT_LIST):
50
The Print Process
105
<define all possible variables>
(LlDefineVariableStart,
LlDefineVariable,
LlDefineVariableExt,
LlDefineVariableExtHandle)
<define all possible fields>
(LlDefineFieldStart,
LlDefineField,
LlDefineFieldExt,
LlDefineFieldExtHandle)
<define options>*
(LlSetPrinterDefaultsDir)
<start print>
(LlPrintStart,
LlPrintWithBoxStart)
<define options>
(LlPrintSetOption,
LlPrintSetOptionString,
LlSetPrinterDefaultsDir,
LlPreviewSetTempPath)
<let user change options>*
(LlPrintOptionsDialog,
LlPrintOptionsDialogTitle,
LlPrintSelectOffsetEx,
[LlPrinterSetup])
<define constant variables>
(LlDefineVariable,
LlDefineVariableExt,
LlDefineVariableExtHandle)
<print variables> (print all objects)
LlPrint
<while "page full" warning (LL_WRN_REPEAT_DATA) do>
LlPrint
<repeat >
{
<define fields>
(LlDefineField,
LlDefineFieldExt,
LlDefineFieldExtHandle)
<print row>
(LlPrintFields)
<while "page full" warning (LL_WRN_REPEAT_DATA) do>
<define page specific variables>*
(LlDefineVariable,
LlDefineVariableExt,
LlDefineVariableExtHandle)
<re-print>
(LlPrint)
49
Programming Using the API
106
(LlPrintFields)
<goto next data record>
<give progress report>*
(LlPrintSetBoxText,
LlPrintGetCurrentPage,
LlPrintGetOption)
}
<until
-error or
-no data records left or
-user abort
>
<Print final footer and all linked objects>
>
(LlPrintFieldsEnd)
<while "page full"-warning (LL_WRN_REPEAT_DATA) do>
o>
(LlPrintFieldsEnd)
<end print>
(LlPrintEnd)
5.4.4
Annotations
Starting Print: Reading the Project File
ile
Before the print can be started, it is important to know which project is to be loaded
and which variables are to be made available.
After the optional dialog where the user can choose the project file,
LlSelectFileDlgTitleEx(), all variables which are to be included in this project must be
defined. If List & Label evaluates an expression in which there is an unknown variable,
then it ends the loading process and passes back the corresponding error code. The
definition of variables is programmed in the same way as the definitions before
calling the Designer.
In the case of a list project (LL_PROJECT_LIST), the corresponding fields must also be
be
defined in order to avoid an error.
Once you have called
LlPrintWithBoxStart(hJob, LL_PROJECT_LABEL, aczProjectFile,
,
LL_PRINT_NORMAL,
LL_BOXTYPE_BRIDGEMETER, hWindow, "my test");
and no error is returned from this function, List & Label has read the definition of the
the
project and is ready to print. The printer is, however, not initialized yet - this is done
e
with the first function call which starts the printing job.
If you want to allow the user to change the print parameters, the corresponding
dialog is called using:
51
The Print Process
107
LlPrintOptionsDialog(hJob, hWindow, "Print Parameter");
With LlSetOption() and LlSetOptionString() standard values for that particular printout
intout
can be given, for example
LlPrintSetOption(hJob, LL_OPTION_COPIES, LL_COPIES_HIDE);
suppresses the "copies" query in the print options dialog.
If "Save options permanently" has been checked in the dialog then the chosen printer
setting is saved in a so-called "printer definition file". Initially, the printer and layout is
yout is
determined in the Designer (menu: Project > Page Layout). If this file is not found,
ot found,
then the Windows standard printer is used. Further information on this can be found
in chapter "List & Label Files".
.
List Projects: Important Things to Note
Note
Variables - in the case of list projects - are values which remain constant for one
one
page, and fields are the record-dependent data. These are printed using
LlPrintFields().
When calling LlPrint() the objects that are not lists are printed, as well as the list
st
headers (if the option LL_OPTION_DELAYTABLEHEADERLINE is not set, otherwise
e
the table header will be delayed until the first data line is to be printed). List & Label
then expects the records to be defined.
With every LlPrintFields() it is tested whether the data record to be printed fits onto
to
the current page. If this is not the case, LL_WRN_REPEAT_DATA is returned, which
h
indicates that a new page should be started. In this case don't increment the record
pointer.
When the table is full, the variables for the next page must be defined before calling
alling
LlPrint(), as with this LlPrint() any linked objects are printed, the new page is started
tarted
and - see above - the objects on the new page including list headers printed.
ed.
A forced page break is possible by calling LlPrint() at any time, which ends the
ds the
present page if this has already been partially filled.
Copies
"Copies" can mean two different kinds of copies:
a) Copies of labels usually do not mean that multiple pages should be printed, but
the copies should be located on labels next to each other.
To support this, get the number of copies before the first LlPrint() call so that you
u
know how many copies should be printed of each label, and set the copies for List &
Label to 1, so that List & Label does not use printer copies too.
46
Programming Using the API
108
// user can change the number of copies...:
LlPrintOptionsDialog(hJob,hWnd,"Printing...");
nCopies = LlPrintGetOption(hJob,LL_PRNOPT_COPIES);
LlPrintSetOption(hJob,LL_PRNOPT_COPIES,1);
Then, print the requested number of labels:
for (nCopy = 1; (nCopy < nCopies) && (nError == 0); ++nCopy)
{
nError = LlPrint(hJob);
// support AUTOMULTIPAGE (usually memos in file cards)
while (nError == LL_WRN_REPEAT_DATA)
nError = LlPrint(hJob);
}
b) True copies of the sheets, that is, identical pages. This kind of copies is directly
handled by List & Label, so no special handling from the developer is necessary.
Speed Optimization
a) Application optimization
At first, variable definitions which are to be constant during printing, can be pulled
d
out of the print loop. If you want to always print your company name in the letter
er
head with lists, it's best to define it outside the loop before LlPrintWithBoxStart().
b) Is the variable / field used?
You can also query which variables or fields are used in the expressions. If the
number of potential variables or fields is much bigger than the actually used number
er
or getting the data values is complex (sub queries, calculations, etc.) using these
functions is worth it. Calling LlGetUsedIdentifiers() returns all variables and fields
s
used in the project. LlGetUsedIdentifiersEx() furthermore allows to differentiate
ate
between the type (variable or field).
You should call this function before print start and later only pass the fields or
r
variables from your data source which will actually be used.
c) Global "Dummy"-job
Some of the system libraries (e.g. riched20.dll) used by List & Label seem to cause
resource losses under certain circumstances. These are very small but incur with
every load and unload of the DLL.
These DLLs are loaded or unloaded by List & Label with every open or close of the
"first" job. Therefore you should avoid a frequent LlJobOpen() / LlJobClose() in your
ur
application or to start a dummy job at start and keep it open until the end. The
permanent loading and unloading of the DLLs is avoided and besides the achieved
speed optimization also the resource losses won't occur anymore.
53
Printing Relational Data
109
5.5
Printing Relational Data
List & Label offers a convenient way of designing projects with multiple relationally
linked database tables (hierarchical reports). The report container is also the easiest
way for the user to work with multiple tables, charts crosstabs or charts in table
columns. The rest of this chapter handles working with LL_PROJECT_LIST type
e
projects. LL_PROJECT_LABEL or LL_PROJECT_CARD type projects support exactly
actly
one table and an arbitrary number of sort orders for this table that can be set and
retrieved just as for LL_PROJECT_LIST projects.
In the following we use "table" as a synonym for a group of matching fields in
the List & Label-Designer or in the "Objects" tool window. You are not restricted
ted
to "real" databases - it is also possible to display a class array or dynamically
lly
created data in a "table", or all member variables of a class. In the List & Label-
Designer you will work with just one "report container object". This object can
contain multiple tables, crosstabs and charts.
Once you have added single tables with LlDbAddTable(), your users can edit the
he
structure in the "Objects" tool window. You will find further information on how to
design the report container object in the corresponding chapter of the Designer
manual. This chapter focuses on how to control such designs.
Examples of how to use multiple tables for the most common programming
languages are included in the installation.
5.5.1
API Functions Needed
d
The name of the API functions needed to control this functionality begin with LlDb…
or LlPrintDb…. You can add tables (LlDbAddTable()), define sortings for the tables
es
(LlDbAddTableSortOrder()) and define relations between tables (LlDbAddTable-
-
Relation()).
At print time you can query the currently active table (LlPrintDbGetCurrentTable()) as
s
well as the currently active relation and sort order (LlPrintDbGetCurrentTableSort-
-
Order(), LlPrintDbGetCurrentTableRelation()). You will find detailed descriptions later
r
in this chapter.
5.5.2
Calling the Designer
First all tables have to be declared to List & Label, so that they can be inserted into
the project:
LlDbAddTable(hJob, "", ""); // delete existing tables
LlDbAddTable(hJob, "Orders", "ORDERS");
LlDbAddTable(hJob, "OrderDetails", "ORDER DETAILS");
The first parameter is the usual job handle of the List & Label job. The second
parameter is the table ID, which will be returned during printout by
44
Programming Using the API
110
LlPrintDbGetCurrentTable(). The third parameter is the display name of the table in
n
the Designer. If you pass NULL or an empty string, the table ID will be used as
display name as well.
A special role is assigned to the table name "LLStaticTable". This is reserved and
can be used for the insertion of 'static' contents (fixed texts or contents of
variables, chart signatures etc.). This type of static table is then available as
"Free content" data source and can only be filled with data lines by the user in
in
the Designer. You must react accordingly to the table in your code - a detailed
d
explanation is provided in the Printing subchapter.
In the next step the relations between the tables will be defined. List & Label does
not directly differ between different types relationships (n:m, 1:n) – you declare a
a
relation with a relation ID which can be queried at print time:
LlDbAddTableRelation(hJob, "OrderDetails", "Orders",
"Orders2OrderDetails", NULL);
With this command, you have established a relationship between the child table
"OrderDetails" and the parent table "Orders". In this case only the ID of the relation
was passed and will be displayed in the Designer.
Finally you can pass sort orders for the tables. Again, you define a unique ID for every
sort order that can then be queried at print time:
LlDbAddTableSortOrder(hJob, "Orders", "OrderDate ASC",
"Order Date [+]");
LlDbAddTableSortOrder(hJob, "Orders", "OrderDate DESC",
"Order Date [-]");
This allows the user to choose one of these sort orders (as well as the default
"unsorted") in the Designer. If you use LlDbAddTableEx() to define the tables, you can
can
also support multiple (stacked) sortings.
The remaining action when calling the Designer is analogous to the "normal" call, i.e.
the complete scheme for calling the Designer with multiple tables looks like this:
<open job>
(LlJobOpen, LlJobOpenLCID)
<define List & Label-settings>
(LlSetOption,
LlSetOptionString,
LlSetDebug,
LlSetFileExtensions,
LlSetNotificationMessage,
LlSetNotificationCallback)
Documents you may be interested
Documents you may be interested