Professional Documents
Culture Documents
L A Z A R U S,
O X Y G E N E,
S M A R T M O B I L E,
A N D
P A S C AL
R E L A T E D
L A N G U A G E S
A N D R O I D,
I O S,
M A C,
W I N D O W S
&
L I N U X
BLAISE
BLAISE PASCAL
PASCAL MAGAZINE
MAGAZINE 44
BY BOIAN MITOV
DATABASE WORKBENCH 5
T H E S W I S S A R M Y K N I F E F O R D ATA B A S E S
BY PETER VAN DER SMAN
TIPS AND TRICKS WITH KBMMEMTABLE
BY KIM MADSEN
BLAISE
BLAISE PASCAL
PASCAL MAGAZINE
MAGAZINE 44
D E L P H I,
L A Z A R U S, S M A R T M O B I L
A N D
P A S C A L
R E L A T E D
L
A
N
F O R
A N D R O I D,
I O S, M A C,
W I N D O W S
E
S T U D
G
U
A
G
& L I N U X
I
E
O,
S
CONTENTS
Articles
OVERVIEW OF DELPHI TO DELPHI HISTORY
EUCLIDES
AN AGE PUZZLE
BY DAVID DIRKSE
ARDUINO: THE VISUINO PROJECT - PART 4
INTERNET OF THINGS - WITH ARDUINO AND DELPHI
PAGE 6
PAGE 7
PAGE 8
BY BOIAN MITOV
DATABASE WORKBENCH 5
T H E S W I S S A R M Y K N I F E F O R D ATA B A S E S
BY PETER VAN DER SMAN
TIPS AND TRICKS WITH KBMMEMTABLE
BY KIM MADSEN
PAGE 12
PAGE 28
PAGE 39
Advertisers
BETTER OFFICE
COMPONENTS 4 DEVELOPERS 48
COMPUTER MATH & GAMES 4/5
DANIEL TETI DELPHI COOKBOOK 11
EVERS 36
NEW BLAISE LIBRARY 38
PASCON DELPHI 37
UPSCENE 35
VISUINO MITOV 27
Stephen Ball
http://delphiaball.co.uk
@DelphiABall
Peter Bijlsma
-Editor peter @ blaisepascal.eu
Marco Cant
www.marcocantu.com
marco.cantu @ gmail.com
David Dirkse
www.davdata.nl
E-mail: David @ davdata.nl
Benno Evers
b.evers
@everscustomtechnology.nl
Bruno Fierens
www.tmssoftware.com
bruno.fierens @ tmssoftware.com
Primo Gabrijeli
www.
primoz @ gabrijelcic.org
Cary Jensen
www.jensendatasystems.com
http://caryjensen.blogspot.nl
Max Kleiner
www.softwareschule.ch
max@kleiner.com
John Kuiper
john_kuiper@kpnmail.nl
Wagner R. Landgraf
wagner @ tmssoftware.com
Kim Madsen
www.component4developers
Jeremy North
Andrea Raimondi
Rik Smit
rik @ blaisepascal.eu
Bob Swart
www.eBob42.com
Bob @ eBob42.com
jeremy.north @ gmail.com
Daniele Teti
www.danieleteti.it
d.teti@bittime.it
Please note: extra space characters have been deliberately added around the @ symbol in
these email addresses, which need to be removed if you use them.
editor @ blaisepascal.eu
Authors - Christian name in alphabethical order
A
B
C
D
F
G
H
J
Andrea Raimondi ,
Stephen Ball, Peter Bijlsma, Dmitry Boyarintsev
Michal Van Canneyt, Marco Cant,
David Dirkse, Daniele Teti
Bruno Fierens
Primo Gabrijeli, Mattias Gaertner
Fikret Hasovic
Cary Jensen
L
K
M
N
O
P
S
Z
Editor - in - chief
Detlef D. Overbeek, Netherlands Tel.: +31 (0)30 890.66.44 / Mobile: +31 (0)6 21.23.62.68
News and Press Releases email only to editor@blaisepascal.eu
Editors
Peter Bijlsma, W. (Wim) van Ingen Schenau, Rik Smit,
Correctors
Howard Page-Clark, James D. Duff
Trademarks
All trademarks used are acknowledged as the property of their respective owners.
Caveat Whilst we endeavour to ensure that what is published in the magazine is correct, we cannot accept responsibility for any errors or omissions.
If you notice something which may be incorrect, please contact the Editor and we will publish a correction where relevant.
Subscriptions ( 2013 prices )
1: Printed version: subscription 65.-- Incl. VAT 6 % (including code, programs and printed magazine,
10 issues per year excluding postage).
2: Electronic - non printed subscription 45.-- Incl. VAT 21% (including code, programs and download magazine)
Subscriptions can be taken out online at www.blaisepascal.eu or by written order, or by sending an email to office@blaisepascal.eu
Subscriptions can start at any date. All issues published in the calendar year of the subscription will be sent as well.
Subscriptions run 365 days. Subscriptions will not be prolonged without notice. Receipt of payment will be sent by email.
Subscriptions can be paid by sending the payment to:
ABN AMRO Bank Account no. 44 19 60 863 or by credit card: Paypal
Name: Pro Pascal Foundation-Foundation for Supporting the Pascal Programming Language (Stichting Ondersteuning Programeertaal Pascal)
IBAN: NL82 ABNA 0441960863 BIC ABNANL2A VAT no.: 81 42 54 147 (Stichting Programmeertaal Pascal)
Subscription department Edelstenenbaan 21 / 3402 XA IJsselstein, The Netherlands / Tel.: + 31 (0) 30 890.66.44 / Mobile: + 31 (0) 6 21.23.62.68
office@blaisepascal.eu
Copyright notice
All material published in Blaise Pascal is copyright SOPP Stichting Ondersteuning Programeertaal Pascal unless otherwise noted and may
not be copied, distributed or republished without written permission. Authors agree that code associated with their articles will be made
available to subscribers after publication by placing it on the website of the PGG for download, and that articles and code will be placed on
distributable data storage media. Use of program listings by subscribers for research and study purposes is allowed, but not for commercial
purposes. Commercial use of program listings and code is prohibited without the written permission of the author.
DAVID DIRKSE
presales at
www.blaisepascal.eu/DavidDirkse/ComputerMath_Games.html
procedure ;
var
begin
for i := 1 to 9 do
begin
end;
end;
www.blaisepascal.eu/DavidDirkse/ComputerMath_Games.html
DAVID DIRKSEs
DELPHI XE8
DELPHI
released February 14, 1995
The roots of Turbo Pascal v1.0 started in Denmark. The first step,
in 1981, was the Blue Label Software Pascal Compiler - BLS Pascal
Compiler v1.2, copyright 1981 by Poly-Data microcenter ApS,
Strandboulvarden 63, DK 2100 Copenhagen - written by Anders
Hejlsberg for the NASCOM kit computer.
Turbo Pascal
Developer(s) Anders Hejlsberg while
working at Borland
Operating system CP/M, CP/M-86, DOS,
Windows 3.x, Macintosh
Platform 8080/Z80, 8085, x86
Lisa - Pascal
Windows
Niklaus Wirth
Archimedes Mathematician
Archimedes of Syracuse was an Ancient
Greek mathematician, physicist, engineer,
inventor, and astronomer.
He is regarded as one of the leading scientists
in classical antiquity. Wikipedia
Born: 287 - 212 BC, Syracuse, Italy
Plato in Classical Attic;
428/427 or 424/423 348/347 BC)
was a philosopher, as well as mathematician,
in Classical Greece.
Euclid Mathematician
Born Mid-4th century BC - 3rd century BC
Residence Alexandria, Hellenistic Egypt
Fields Mathematics Known for
Euclidean geometry / Euclid's Elements
Euclidean algorithm
Pythagoras Philosopher
Pythagoras of Samos was an
Ionian Greek philosopher, mathematician,
and founder of the religious movement
called Pythagoreanism.
Born: 571 - 495 BC,
Thales Philosopher
Thales of Miletus was a pre-Socratic Greek philosopher
from Miletus in Asia Minor and one of the
Seven Sages of Greece. Many, most notably Aristotle,
regard him as the first philosopher in the Greek tradition.
Born: 624 BC - 546
DELPHI
The cult of Apollo at
Delphi probably
dates back to
the 700s B . C .,
ISSUE 40
Page 9
WATER CLOCK - CHINA - BEGINNING OF TIME (BC 4000)
Some authors claim that water clocks appeared in China
as early as 4000 BC
EUCLIDES
Euclid (300 BC), sometimes called
Euclid of Alexandria to distinguish him
from Euclid of Megara, was a Greek
mathematician, often referred to as the
"Father of Geometry". He was active in
Alexandria during the reign of Ptolemy
I (323283 BC). His Elements is one of
the most influential works in the history
of mathematics, serving as the main
textbook for teaching mathematics
(especially geometry) from the time of
its publication until the late 19th or early 20th century.
In the Elements, Euclid deduced the principles of what is
now called Euclidean geometry from a small set of
axioms. Euclid also wrote works on perspective, conic
sections, spherical geometry, number theory and rigor.
QUESTIONS:
1.
2.
White's
SOLUTION
THE PROGRAM
procedure showSolutions;
begin
clearpaintbox;
with form1.PaintBox1.Canvas do
begin
brush.style := bsClear;
font.Color := fontcolor;
font.Name := fontname;
font.Height := fontheight;
for n := 1 to solutionNr do
if n <= solutionsperdisplay then
begin
y := (n-1)*fontheight + ymarge;
for i := 0 to 3 do
begin
x := columns[i];
case i of
0 : s := inttostr(n);
1 : s := inttostr(solutions[n].birthyear);
2 : s := inttostr(solutions[n].age);
3 : if solutions[n].birthdaypassed
then s := 'yes'
else s := 'no';
end;//case
textout(x,y,s);
end;
end;//for..if
end;//width
end;
digitsum : byte ;
birthyear : word;
30,00
including VAT
39
including
the
printed
book,
ebook
and shipping
DI
HOICE A
SC
R BLAISE
PASCAL
MAGAZINE
AZING
M
TO
Delphi Cookbook
Daniele Teti
http://www.blaisepascal.eu/daniele_teti_book/DanieleTeti.html
PAGE - 1/15
12
13
6
14
15
16
17
18
19
20
21
22
23
24
25
CONCLUSION
This article has given you enough information to
start communicating with one or more Arduino
devices over wired network, or Internet from your
Delphi code. This is your first introduction to the
exciting world of Internet of Things. In the
following articles you will learn how to
communicate with Arduino over WiFi, and how to
make multiple Arduino boards to talk to each
other.
26
What is Visuino?
Visuino is the latest innovative software from Mitov Software. A visual programming
environment allowing you to program your Arduino boards. Although it currently
supports the official Arduino boards, it is not restricted to their support alone and
requests to support new hardware are welcome.
INTRODUCTION
What is Arduino?
The components found in the Visuino software represent their hardware components and you
will easily be able to create and design your programs using drag and drop. No equipment or
hardware is needed to run the software in design mode. Once you have completed the design,
you can connect Arduino board upload and run it.
For those people who are not strong on writing code then designing, compiling and creating
Arduino programs has never been easier! Why waste time on creating code when we have done
all the hard work for you already? You have your Arduino board, and great hardware design,
see it running in minutes, not hours!
Currently we are running a Beta program which you can be part of by joining our Google group.
Join the group now to download and test the software or send an email to mitov@mitov.com.
www.visuino.com
DATABASE WORKBENCH 5
PAGE 1/8
28
DATABASE WORKBENCH 5
THE SWISS ARMY KNIFE FOR DATABASES PAGE 2/8
29
DATABASE WORKBENCH 5
THE SWISS ARMY KNIFE FOR DATABASES PAGE 3/8
30
DATABASE WORKBENCH 5
THE SWISS ARMY KNIFE FOR DATABASES PAGE 4/8
More than enough information. You might
notice the Column Type for EMPNO for the
column EMP_NO. Looks a bit odd, this has to
do with defining your own types in Firebird.
In the schema (figure 1) you can find these
definitions under Domains. For our database
has no less than 15 types defined.
Our EMPNO' turnes out to be of type
SmallInt. The other tabs in the Table
Properties form show more information about
the table. The tab DLL gives you all the code
needed to create the table.
This design is used consequently. Double
clicking in the Navigator (figure 1) on
different items will result in similar screens to
popup. No matter if it is about a Domain, a
View, an Index, a Constraint, a Trigger,
an Exception or any other item, you'll get Tab
with relevant detail, and a tab DDL giving
you the code to implement in the database. In
fact, you don't really need this code, as we'll see
later on, but it can be a handy way to check up
what you have done with an example
implementation. Unless you're the type
inventing all by yourself and never looking at
example code?
The temptation is strong to directly start making
all kind of changes. The schema (figure 3) shows
us an emp_NO, proj_ID en job_CODE. Of
course there can be puristical reasons the make
the distinction between NO, ID and
CODE, but perhaps you would prefer to
change this all to ID. The good news is we
actually can easily make these changes in the
screen as shown in figure 4. Just change
EMP_NO to EMP_ID. The bad news is it
won't be of any use.
The diagram in the diagram editor is not
connected to a physical database.
The consequence is we cannot pass our changes
made to the database. In fact this is a good thing.
In a designing phase it could be a nice idea to
make the proposed changes, making them in an
existing database can be quite disastrous.
There's every change that these fields play a part
in a trigger, a view or a procedure. In fact, we
can change fieldnames using Database
Workbench: just open a table from the navigator
and change the name of the field. In this screen
there is this handy button Create/Alter Table.
Nine times out of 10 this will result in a message,
from Firebird, telling you the update was
unsuccessful due to data integrity.
31
DATABASE WORKBENCH 5
THE SWISS ARMY KNIFE FOR DATABASES PAGE 5/8
Note I did'nt mind about the type definition
for my fields. No reason to bother about it
right now, we can just focus in getting the
general structure right.
Looking to our design we note the field
Language appearing in two tables. So
most likely we should consider adding a
new table/entity Languages. And of
course we must find a way to bind the
Articles to the proper Blaise edition.
The most flexible way to do it is using a new
table making the connecting between the
two tables. It gives the editor a way to
republish an article . Speaking about this:
most articles in effect will be republished in
different editions, that is to say, in
translation. So apart from the writer of the
original version there can be a translator
too. And we should be able to refer to the
original article. So we can add a table
Translators, but the easy way is to use the
table Writers for this purpose as well. This
directly solves the problem of persons who
both write articles and translate articles as
well. And besides, writing an article or
translating, both are a kind of writing. And,
in case you had noticed, we now have a
reason to use author in the Articles table
referring to a table writers. And a fieldname
date is a bit ambiguous, so let's change it to
DatePublished right away.
Let's make some connections as well. The icons
as shown in figure 5 point out we can use three
kinds of links: Identifying Relationship,
Non-Identifying Relationship and Documentation
link . The last one is meant to make connections
just for clarifications in the diagram, not for
implementing in the database. After some not
too hard labour it could look like this.
In the real world this would be the moment to
put it aside and to look at it again at a later point
of time and discussing it with a colleague.
However, in the world of Blaise the next edition
is coming soon, so we continue with our schema
right away. We do a bluff and ask Database
Workbench to convert this schema to a
Physical Diagram for Firebird. The result is a
pile of error messages: this is the time we should
point out the datatypes we are going to use.
Further on we did make connections between
tables, but I didn't really bother working them
out. The good news is we don't have to search
ourselves for missing data, we just get a list of
items to resolve. We can't blame the program it
initially is a long list. The following schema
shows the more adapted conceptual schema
(figure 7).
32
DATABASE WORKBENCH 5
THE SWISS ARMY KNIFE FOR DATABASES PAGE 6/8
Where needed I provided for a unique key field
in all tables. All fields are given a type. Just for
showing the differences I used some different
types. And the connections are filled in with
relevant information. To get a nice picture I
removed the connection between
OriginalVersion and ArticleID from the
Diagram. But it still exists. To show it a Subdiagram can be used:
33
DATABASE WORKBENCH 5
THE SWISS ARMY KNIFE FOR DATABASES PAGE 7/8
The reference from OriginalVersion back to the
table Blaise is called a Constraint. It is build
using the following screen.
34
DATABASE WORKBENCH 5
THE SWISS ARMY KNIFE FOR DATABASES PAGE 8/8
Of course now we have to doubel check the total
schema again and adapt it where necessary.
We could add a Check Constraint , an Index or a
Trigger where desired.
After doing this we can use the schema to build a
real database. We have two options available.
The hard way is to use the option Extract DDL For
ALL Objects . It provides us a SQL script to be
used for generating the database. The advantage
might be you can recheck the script before
running it and/or maybe add some very special
code you couldn't add using the program.
And the script can be useful for documentation
purposes. But we also can use the easy way.
Just take the option Create Database from
Diagram and follow the instructions in the
Wizard. It will ask you the location of the
database, username and password and so on.
When we're done the database will be created
and the script will run in order to create your
structured tables. In anything does go wrong
with the script it will pop-up in the script editor
so you can make your adjustments. .
EPILOGUE
35
COMPONENTS
DEVELOPERS
CUSTOM TECHNOLOGY
b.evers@eversct.nl
36
http://www.blaisepascal.eu/DucthPascon/Pascon2015UK.html
L I B R A R Y 2 0 1 5
CLICK TO OPEN
ITEM (NUMBER)
CLICK TO OPEN
THE TOTAL
OVERVIEW OF ALL
ITEMS IN ONE
10
12
6
7
8 9
11
13
18 19
16
17
14 15
27
20
26
25
21
22 23 24
28
34 35 36
33
29 30
31 32
40 41
38
37
42
39
CLICK TO OPEN
A BROWSER WITH
URL
CLICK TO OPEN
DELPHI URL
CLICK TO OPEN
ITEM (NUMBER)
CLICK TO OPEN
THE TOTAL
OVERVIEW OF ALL
ITEMS IN ONE
WINDOWS 10
www.blaisepascal.eu/subscribers/UK/UK_CD_DVD_USB_Department.html
Issue Nr 6 2015 BLAISE PASCAL MAGAZINE
38
expert
Delphi
COMPONENTS
DEVELOPERS
PAGE 1/9
var
fldID,
fldName,
fldAddress:TField;
begin
HOW TO SEARCH?
PAGE 2/9
PAGE - 2/4
v:=mt.Lookup('Name','Jens Hansen','Address');
if not VarIsNull(v) then
MessageDlg('Found record.Address='+v);
You can also search for partial values
if mt.Locate('Name','Jens',[loPartialKey]) then
MessageDlg('Found record.
Address='+mt.FieldByName('Address').AsString);
How to search on multiple fields?
if mt.Locate('Name','Jens',[loPartialKey])
then MessageDlg('Found record. Address='+mt.FieldByName('Address').AsString);
Let kMemTable automatically create indexes for you
to improve search performance
mt.AutoAddIndexes:=true;
mt.AddIndex('iMyIndex','Name',[ixDescending]);
mt.IndexName:='iMyIndex';
Or even easier:
mt.SortOn('Name',[mtcoDescending]);
Arithmetic:
=
Equal
>
Greater than
<
Less than
>= Greather than or equal
<= Less than or equal
<> Not equal
IS NULL
True if expression is null
IS NOT NULL True if expression is not null
NOT
Negates the boolean expression
OR
True if one of the two boolean
expressions are true
AND
True if both the two boolean
expressions are true
IN ( ... )
True if the result of the left side
expression is found in the list
of values.
LIKE '....'
True if the left side string
expression is matching the
wildcards in the right side string.
COMPONENTS
DEVELOPERS
40
UPPER( expression )
Convert string expression to uppercase.
LOWER( expression )
Convert string expression to lowercase.
SUBSTRING( expression , startpos , count )
Extract a substring from the expression string
starting at startpos and with max length of count
chars. The count parameter can be omitted, in
which case is means the rest of the string.
TRIM( expression ) Trim string expression for all
leading and trailing spaces.
TRIMLEFT( expression ) Trim string expression
for all leading spaces.
TRIMRIGHT( expression ) Trim string expression
for all trailing spaces.
GETDATE Returns a floating point date/time
value for current time.
YEAR( expression ) Return the year of a floating
point date/time value.
MONTH( expression ) Return the month of the year
(1..12) of a floating point date/time value.
DAY( expression ) Return the day of the month
(1..28/29/30/31) of a floating point date/time
value.
HOUR( expression ) Return the hour (0..23) of a
floating point date/time value.
MINUTE( expression ) Return the minute (0..59) of
a floating point date/time value.
SECOND( expression ) Return the second (0..59) of
a floating point date/time value.
DATE( datestring , formatstring )
Convert the datestring to a floating point
date/time value according to the
SysUtils.FormatDateTime format string .
DATE( expression ) Return the date part of a
floating point date/time value.
TIME( timestring , formatstring )
Convert the timestring to a floating point
date/time value according to the
SysUtils.FormatDateTimeformat string .
TIME( expression ) Return the time part of a
floating point date/time value.
PAGE 3/9
41
COMPONENTS
DEVELOPERS
PAGE 4/9
'__MT__DEFAULT_' (kbmDefaultIndex).
This index is automatically created when you set
IndexFieldNames to a set of fields for which no
index already exist.
'__MT__RANGE_' (kbmRangeIndex).
This index is automatically created when you
apply a range filter.
INDEX INHERITANCE
COMPONENTS
DEVELOPERS
42
PAGE 5/9
mt.Edit.mt.Post;
mt.StartTransaction; // Second level of transaction
mt.Delete;
mt.Rollback;
// Back to the edited record again
mt.Rollback; // Back to the original appended record
edited record.
mt.Rollback; // Ah no.. we will revert to the original
appended record
43
COMPONENTS
DEVELOPERS
mt.Undo;
PAGE 6/9
COMPONENTS
DEVELOPERS
44
PAGE 7/9
mtcpoStructure:
Copies table structure (field definitions) from the
source. Any field definitions you had in your local
If the source dataset has field 'str1' which is a
memory table are removed.
mtcpoOnlyActiveFields:
ftWideString field, while the local
Only field definitions in the source that actually
memorytable has the same field 'str1' defined as a
are represented as a true field, are copied.
ftString field, it will UTF8 encode the data
mtcpoProperties:
coming from the source field before putting it into
Copy over field properties like DisplayWidth,
the destination field. Similarly if a source field is
DisplayLabel, Required, ReadOnly,
of type ftString, but the destination is of type
Visible, DefaultExpression,
ftWideString then an UTF8 decoding will take
Alignment, ProviderFlags, Lookup,
place before putting the value into the destination
LookupCache, LookupDataset,
field.
The same takes place with
LookupKeyFields, LookupResultField,
ftMemo/ftWideMemo
and
KeyFields, DisplayFormat,
ftFixedChar/ftWideFixedChar.
EditFormat, MaxValue, MinValue,
DisplayValues, TransLiterate,
If you already have an existing field structure in
Precision, Currency and BlobType.
mtcpoLookup:
your local memory table, and want to copy fields
Also copy lookup fields from the source dataset.
from a source dataset, where the field names do
mtcpoCalculated:
not match, you can take advantage of field name
Also copy calculated fields from the source
mapping.
dataset.
mtcpoAppend:
Eg.
Append records to the existing records in the
mt . LoadFromDataset ( anotherdataset ,
memory table. This cant be used with
[], 'str1=str_1;int2=int_2' );
mtcpoStructure or mtcpoProperties.
mtcpoFieldIndex: Copy the index position of
This is telling kbmMemTable that the local field
fields to ensure field order is the same as in the
named str1 is called str_1 in the source table etc.
original.
As for option 3, loading from a file, you can use:
mtcpoDontDisableIndexes:
mt.LoadFromFileViaFormat('somefilename',
Default a batch insertion is made, where indexes
someformatinstance) ;
are only updated after all records have been
copied over. However if you for example have an
index with unique constraint on a field, then you
might want to have your copy to stop early, if
there is a duplicate of that field value.
45
COMPONENTS
DEVELOPERS
someformatinstance
sf:=TkbmMemCSVStreamFormat.Create(nil);
try
mt.LoadFromFileViaFormat('.\mydata.csv',sf)
finally
sf.Free;
end;
EXTRACTING DATA
PAGE 8/9
kbmBookMarkFirst,VarArrayOf('fld1','fld2'));
v:=TkbmVariantObject(slData.Objects[i]
).Value;
s:=slData.Strings[i];
end;
COMPONENTS
DEVELOPERS
46
PAGE 9/9
STATISTICS
var v:variant;
begin
v:=mt.Aggregate('fld1:SUM');
end;
fldCountry
fldSales
US
10
GB
20
US
30
DE
15
DE
GB
10
US
12
var
v:variant;
begin
v:=mt.Aggregate('fld1:SUM;fld1:STDDEV;fld1:COUNT');
end;
20
15
GB
30
20
US
52
30
var mtGrouped:TkbmMemTable;
begin
mtGrouped:=TkbmMemTable.Create(nil);
try
mt.GroupBy(mtGrouped,
'fldCountry',
'fldCountry;fldSales:SUM;fldSales:MAX;fldSales:COUNT');
Go have fun!
Kim Madsen
finally
mtGrouped.Free;
end;
end;
47
COMPONENTS
DEVELOPERS
Warning!
COMPONENTS
DEVELOPERS
EESB, SOA,MoM, EAI TOOLS FOR INTELLIGENT SOLUTIONS. kbmMW IS THE PREMIERE N-TIER PRODUCT FOR DELPHI /
C++BUILDER BDS DEVELOPMENT FRAMEWORK FOR WIN 32 / 64, .NET AND LINUX WITH CLIENTS RESIDING ON WIN32 / 64,
.NET, LINUX, UNIX MAINFRAMES, MINIS, EMBEDDED DEVICES, SMART PHONES AND TABLETS.