C++ Builder, EVENTS

background image

Document :: Simple DataBase Events.
Author :: John Kirkpatrick

From "The Bits..." the C++Builder Information & Tutorial Site
http://www.cbuilder.dthomas.co.uk

©"The Bits..." 1998. ©Simon Rutley-Frayne December 1998.
All rights reserved, please see details at the end of the tutorial.

Edited and Converted to PDF format by Simon Rutley-Frayne

No liability is accepted by the author for anything which may occur whilst following this tutorial

This project has been built and tested under Win95.

Colour Coding:

Information displayed in this colour is information provided by my copy of BCB.

Information displayed in this colour is information you need to type.

Information displayed in this colour is information which is of general interest.

********************************************************************************

I freely acknowledge that the idea for the program described in this tutorial came from Charlie Calvert. His version of the
program is described in his excellent Borland C++ Builder 3 Unleashed. (SAMS Publishing, 1998). However, this program goes
slightly further than Charlie’s and looks at what happens when two or more datasets are linked in a master-detail relationship.

Since we’ll be mucking around with some example datasets supplied with BCB, it’s a good idea to take a backup copy of the files
BCDEMOS folder before starting, so that you can get back to ground zero if things go horribly pear-shaped whilst working
through this tutorial. By default, BCDEMOS points to either the

C:\Program Files\Borland\Borland Shared\Data

folder

(version 3) or *** (Version 1).

1

Database events

Database events are far and away the most effective way of controlling access to the information in your database, and of ensuring
the integrity of it. A well-designed set of responses to database events will make it impossible for users to mangle their data,
provide an easy an effective way for them to recover from their mistakes. Events also give you, as the programmer, a simple way
to write efficient, reliable and flexible code.

The best way to understand database events is to see how and when the VCL generates them. So, we’ll start off by writing a
simple application that does little more than monitor the events generated by scrolling through a simple two table database and
modifying a few records in it.

In this tutorial, we’ll look at the events that occur as you move around a dataset, and as you modify delete and add records to it.
The application we’ll build won’t actually do anything more than provide a window through which we can look at the activity
going on behind the scenes. We may not arrive anywhere that is practically useful, but I hope you’ll find the journey
educational…

A more detailed look at some database events, especially those related to posting records to a database, especially those that occur
when things go wrong, may be the subject of a future tutorial.

background image

2

Creating the main form

Start a new project in BCB.

I like to use group boxes to partition forms up into areas containing related controls. If you do too, drop two TGroupBoxes onto
the form, one on the left and the other on the right. If not, don’t worry, they don’t play any active part in what follows.

Set the caption of the right-hand group box to Events and the caption of the left-hand one to Master. (We won’t be creating a
detail table just yet, but it pays to prepare.)

Drop a TDBGrid and a TDBNavigator onto the Master group box, followed by a TTable and a TDataSource at the bottom of the
form.

Set the properties of these controls as follows:

TTable
DatabaseName

BCDEMOS

Name

MasterTable

TableName

venues.db

TDataSource
DataSet

MasterTable

Name

MasterSource

TDBNavigator
DataSource

MasterSource

Name

MasterNavigator

TDBGrid
DataSource

MasterSource

Name

MasterGrid

If you’ve done all of that correctly, you should see the DBGrid spring to life when you set the Active property of MasterTable to
true.

If you like, you can build and run the project at this point and check that you can move through the venues table and modify
delete and insert records using the navigator buttons, but it’s a pretty dull application at the moment. Let’s start to make things a
little more interesting….

Drop a TListBox and a TCheckBox onto the Events group box, and a TButton and a TOpenDialog just below it. Set these
controls up as follows:

TListBox
Name

EventsListBox

TCheckBox
Caption

Record events

Checked

true

Name

EventsCheckBox

TButton
Caption

Save

Name

SaveButton

TOpenDialog
DefaultExt

txt

FileName

events.txt

Filter

Text files (*.txt)|*.txt|All files (*.*)|*.*

Name

OpenDialog

Options

ofOverwritePrompt

true

OfPathMustExist

true

At this point, your form should look something like this:

background image

shapeType75fFlipH0fFlipV0pib94a5c9e65f0f0d1b8bf6382b258656dcfLine0
94a5c9e65f0f0d1b8bf6382b258656dc

3

Recording database events

Even the simplest of database operations can generate a huge number of events, so one really useful thing to have will be the
ability to save event histories to file. That way, we can explore them at leisure. So, at last, here’s some code to write - in the
OnClick event handler of SaveButton:

void __fastcall TForm1::SaveButtonClick(TObject *Sender)
{

if (OpenDialog->Execute())
{
ofstream out(OpenDialog->FileName.c_str());
for (int i=0; i < EventsListBox->Items->Count; i++)
out << EventsListBox->Items->Strings[i].c_str() << endl;
out.close();
}

}

(Don’t forget to add

#include <fstream.h>

to the top of your source file. fstream.h is the header that contains the prototypes for

the classes, functions and operators relating to file input and output.)

We’ll also need a small utility routine to make displaying events to EventsListBox as simple as possible. Add

void __fastcall NoteEvent(TObject* Sender, AnsiString Text="");

to the private: section of the TForm’s class definition in the header file, and the following code to the end of the TForm’s cpp file

void __fastcall TForm1::NoteEvent(TObject *Sender, AnsiString s)
{
if (!RecordEventsCheckBox->Checked) return;
EventsListBox->Items->Add(dynamic_cast<TComponent *>(Sender)->Name + ": " + s);
}

If we place a call to NoteEvent in each of the event handlers of the MasterTable and MasterSource components, we’ll have an
easy way of recording both events and the order in which they are generated. All the event handlers of a TTable object take a
TDataSet* object as their first parameter. The event handlers of a TDataSource all take a TObject* as their first parameter. This
is why the first parameter of NoteEvent is a pointer to a TObject. I cast this parameter to a pointer to a TComponent to gain
access to its Name property. Strictly speaking this is slightly dangerous, because not all descendants of TObjects are
TComponents, and therefore not all its descendants will have a Name property. It is possible that NoteEvent might be passed a
value of Sender that does not point to a TComponent. However, I know that all the events we’re interested in will be generated
by users clicking on visible components on the surface of a form. Therefore I know that all Senders will be TComponents, and so
will have Names. It’s a small piece of imprecision that I’m prepared to live with for the sake of simplicity.

The reference to RecordEventsCheckBox is simply to prevent the EventsListBox filling with garbage when we know we’re not
interested in what’s going on. You might like to add a Clear button to the form to help out even more.

Recording the events generated by the MasterTable table is simple. We’ll start by adding a handler for the AfterCancel event.

Select the MasterTable component and go to the Events tab in the Object Inspector. Double-click on the right-hand column. BCB
adds the following code to the TForm’s cpp file:

void __fastcall TForm1::MasterTableAfterCancel(TObject *Sender)
{
}

and the following to the __published: TForm’s .h file:

void __fastcall MasterTableAfterCancel(TObject *Sender);

The Object Inspector also shows that the name of the MasterTable’s AfterCancel event handler has been set to
MasterTableAfterCancel.

We could carry straight on at this point, but it’s worth taking a little time now to make life easier in the future. Later on, we’ll be
adding another TTable to the form and tracking events in both it and the MasterTable. It would be convenient to use the same
event handler for both TTables. We could, if we wanted, simply set this new TTable’s AfterCancel event handler to
MasterTableAfterCancel, but in a more complex project it would be confusing to use an event handler for one control that was
named after another. For example,

background image

NewTable->AfterCancel = MasterTableAfterCancel;

So, let’s rename the MasterTable’s event handler to something that will avoid the confusion later on. Go back to the Events tab
on the Object Inspector and change the name of the AfterCancel event handler to AfterCancel. The TForm’s .cpp and .h files are
also changed:

void __fastcall TForm1::AfterCancel(TDataSet *DataSet)
{
}

and

void __fastcall AfterCancel(TDataSet *DataSet);

OK, now we can move back to the main thrust of this tutorial. Let’s make the event handler do something:

void __fastcall TForm1::AfterCancel(TDataSet *DataSet)
{

NoteEvent(DataSet, "AfterCancel");

}

Now, move through all the MasterTable’s events, adding a handler for each one. Modify the name of each one so that the
reference to MasterTable is removed (we’ll be using them all on the other TTable later on) and add a call to NoteEvent in the
body of each.

The handlers for the events associated with MasterSource and MasterNavigator components are somewhat longer, but just as
straightforward. Here’s the MasterNavigator’s OnClick handler:

void __fastcall TForm1::

NavigatorClick

(TObject *Sender, TNavigateBtn Button)

{

AnsiString s;

switch(Button)
{
case nbFirst:
s = "nbFirst";
break;
case nbNext:
s = "nbNext";
break;
case nbLast:
s = "nbLast";
break;
case nbPrior:
s = "nbPrior";
break;
case nbDelete:
s = "nbDelete";
break;
case nbInsert:
s = "nbInsert";
break;
case nbPost:
s = "nbPost";
break;
case nbCancel:
s = "nbCancel";
break;
case nbEdit:
s = "nbEdit";
break;
case nbRefresh:
s = "nbRefresh";
break;
}
NoteEvent(Sender, s);

}

We don’t need to bother about any of the other DBNavigator events.

The MasterSource’s OnStateChange event handler needs to pick up the State of its corresponding DataSet component:

void __fastcall TForm1::

StateChange

(TObject *Sender)

{

AnsiString s;

switch(dynamic_cast<TDataSource *>(Sender)->DataSet->State)
{
case dsInactive:

background image

s = "Inactive";
break;
case dsBrowse:
s = "Browse";
break;
case dsEdit:
s = "Edit";
break;
case dsInsert:
s = "Insert";
break;
case dsSetKey:
s = "SetKey";
break;
case dsCalcFields:
s = "CalcFields";
break;
case dsFilter:
s = "Filter";
break;
}
NoteEvent(Sender, s);

}

There are some other state constants that are listed in the TDataSetState enumeration, but these states occur only when using
cached updates – which we aren’t – so we can safely ignore them. These extra constants are dsUpdateNew and dsUpdateOld (in
version 1) or dsNewValue, dsOldValue and dsCurValue (in version 3).

Note the cast in the switch() statement:

switch(dynamic_cast<TDataSource *>(Sender)->DataSet->State)

Rather like the cast of the Sender parameter in the NoteEvent routine, I’m assuming that all Senders that trigger StateChange
events are TDataSources. That’s reasonable in this program. At the moment,

switch(MasterSource->DataSet->State)

would serve just as well, but wouldn’t work when we add a detail table to the form.

The OnDataChange event handler needs to check the value of its TField* parameter. The OnDataChange handler is called
whenever the value of any field in the dataset changes. This might be because the user is navigating through the dataset and
changing the current record, or because the user is editing the value displayed in a particular visual control. If a user editing the
value in a control causes the change, Field points to the underlying dataset field. If a new record is being loaded, Field is NULL.

void __fastcall TForm1::

DataChange

(TObject *Sender, TField *Field)

{

AnsiString s = "OnDataChange [";

if (Field == NULL)
s += "NULL]";
else
s += Field->FieldName + "]";
NoteEvent(Sender, s);

}

Phew! That was dull, but now we have something useful. Save the project and run it.

4

A first run of the program

Here’s what you should see in the Events ListBox as soon as the form is shown:

MasterTable: BeforeOpenMasterSource: BrowseMasterSource: OnDataChange [NULL]MasterTable:
AfterOpenMasterTable: AfterScroll

Five events, and we haven’t even done anything yet! The MasterTable’s BeforeOpen event occurs first. Then the MasterTable’s
Open method is called, changing MasterTable’s State from dsInactive to dsActive and triggering MasterSource’s StateChange
event. MasterTable’s Open method then loads the first record in the table, triggering MasterSource’s DataChange event, with a
NULL value for the Field parameter. Next comes MasterTable’s AfterOpen event, and last but by no means least AfterScroll has
its say.

Something interesting happens when you click on a navigator button:

MasterTable: BeforeScrollMasterSource: OnDataChange [NULL]MasterTable: AfterScrollMasterNavigator: nbNext

background image

Even though you clicked the Next button to initiate this series of events, the MasterNavigator’s OnClick event handler is the last
to fire.

Here’s what happened when I changed the value of the Venue field of the first record from “Memorial Field” to “Memorial Field
xxxx”:

MasterTable: BeforeEditMasterSource: EditMasterSource: OnDataChange [NULL]
MasterTable: AfterEdit
MasterSource: OnDataChange [Venue]
MasterSource: OnUpdateData
MasterTable: BeforePost
MasterSource: Browse
MasterSource: OnDataChange [NULL]
MasterTable: AfterPost
MasterNavigator: nbPost

The first four events occurred immediately I pressed the space bar to begin adding the extra characters.

Since MasterTable is in dsBrowse mode by default, the VCL automatically changes its state to dsEdit as soon as I begin typing in
the DBGrid. This triggers the MasterTable’s BeforeEdit event and the MasterSource’s OnStateChange event. Note that the
MasterTable’s AfterEdit event was triggered after the Table had been placed in dsBrowse mode, not after I actually modified the
data.

The remaining seven events were triggered when I clicked on the Post button in the MasterNavigator.

Notice that this time theDataChange handler knows which field was modified and how the MasterTable and MasterSource
components interleave their various notification messages.

When I edit two fields one after the other, without posting the changes to the first before editing the second, the only additional
event that is triggered is an additional OnDataChange event for the second field. The call to the OnDataChange handler for the
first field occurs as I click on the second field’s column in the grid, not when I start to edit the second field.

MasterTable: BeforeEdit
MasterSource: Edit
MasterSource: OnDataChange [NULL]
MasterTable: AfterEdit
MasterSource: OnDataChange [Venue]
MasterSource: OnDataChange [Capacity]
MasterSource: OnUpdateData
MasterTable: BeforePost
MasterSource: Browse
MasterSource: OnDataChange [NULL]
MasterTable: AfterPost
MasterNavigator: nbPost

Deleting a record triggers a different cascade of events…

MasterTable: BeforeDeleteMasterTable: BeforeScrollMasterSource: OnDataChange [NULL]MasterTable:
AfterDeleteMasterTable: AfterScroll
MasterNavigator: nbDelete

…as does adding a new record:

MasterTable: BeforeInsertMasterTable: BeforeScrollMasterSource: InsertMasterTable: OnNewRecordMasterSource:
OnDataChange [NULL]MasterTable: AfterInsertMasterTable: AfterScroll
MasterNavigator: nbInsert
MasterSource: OnDataChange [Venue]
MasterSource: OnDataChange [Capacity]
MasterSource: OnUpdateData
MasterTable: BeforePost
MasterSource: Browse
MasterSource: OnDataChange [NULL]
MasterTable: AfterPost
MasterNavigator: nbPost

Notice that the OnDataChange event doesn’t get called for the VenueNo field, which is an AutoIncrement field. Also, it’s very
important to notice that the value of the VenueNo field doesn’t become available to the VCL (and isn’t displayed in the
MasterGrid) until after the MasterNavigator’s post button is clicked: it’s blank for the entire time the new record is being
inserted. The value of the VenueNo field is first available to BCB immediately before the final occurrence of the MasterTable’s
DataChange event.

5

Adding a calculated field to the MasterTable

background image

Once you have a calculated field in your table, the OnCalcFields event will waste no time in introducing itself to you. This event
really is a social animal, and will take every opportunity it can to see what’s going on.To add a calculated field to the
MasterTable, do the following:

Select the MasterTable component on the form designer and select the Fields editor item on the pop-up menu which appears
when you right-click on it.

shapeType75fFlipH0fFlipV0pib41760bec2e6cf970af29f43446872286fLine0
41760bec2e6cf970af29f43446872286

Now, right-click on the Fields editor itself and select Add new field from this pop-up. Complete the dialog that is now displayed
as illustrated below.

This defines a new calculated field called MaximumIncome in the MasterTable table. Now we need to write the code to populate
the new field. This is what the OnCalcFields event is really for, not adding text to ListBoxes! Let’s assume that the maximum
income that can be generated is £35 per seat in the venue…

void __fastcall TForm1::CalcFields(TDataSet *DataSet)

{
NoteEvent(DataSet, "OnCalcFields");

MasterTableMaximumIncome->Value = MasterTable->FieldByName(“Capacity”)->Value * 35;

}

Now, when you run the program, you get the following:

MasterTable: BeforeOpen
MasterTable: OnCalcFields
MasterSource: Browse
MasterSource: OnDataChange [NULL]
MasterTable: AfterOpen
MasterTable: AfterScroll
MasterTable: OnCalcFields
MasterTable: OnCalcFields
MasterTable: OnCalcFields
MasterTable: OnCalcFields
MasterTable: OnCalcFields
MasterTable: OnCalcFields
MasterTable: OnCalcFields
MasterTable: OnCalcFields
MasterTable: OnCalcFields

The OnCalcFields event is triggered once for each visible row of data in the MasterGrid, although the first call is separated from
the others by the various other events associated with the opening of the table and population of the grid. You can test this out by
changing the size of the grid and rerunning the program.

If you use this small grid to scroll through the table, the OnCalcFields event is triggered once for each new record that is
displayed as you scroll.

MasterTable: BeforeScrollMasterTable: OnCalcFieldsMasterSource: OnDataChange [NULL]MasterTable: AfterScroll

If you edit a field, the OnCalcField event is triggered (twice), as is the OnDataChange event (once) for the calculated field, even
when the calculated field doesn’t depend on the field you’ve modified:

MasterTable: BeforeEditMasterTable: OnCalcFieldsMasterSource: EditMasterSource: OnDataChange [NULL]
MasterTable: AfterEdit
MasterTable: OnCalcFields
MasterSource: OnDataChange [MaximumIncome]
MasterSource: OnDataChange [Venue]

This repeated triggering of the OnCalcFields event can seriously affect the performance of your application if you’re doing a lot
of modifications to the data in the table behind the scenes. If the user doesn’t need to see the calculated fields, you can speed
things up by setting the AutoCalcFields property of the TTable to false before you start the processing. (Don’t forget to set it back
to true once you’ve finished!)

We’ve finished with the OnCalcFields event for now, so delete the MasterTableMaximumIncome field from MasterTable and
delete the corresponding line from the OnCalcFields method of the form. It would also be a good idea to restore the original
version of the venues table that you made before we started.

6

Adding the Events table to the form

We’re going to add the events table to the form now, and then link the two tables so that as you scroll through the venues table,
only those events which take place at the currently selected venue are displayed.

background image

Start off by squeezing the MasterGrid (and corresponding GroupBox if you added one) up a little bit. Then add a TGroupBox,
TDBGrid and a TDBNavigator into the space you’ve just created. Also, drop a second TTable and a second TDataSource at the
bottom of the form. Once you’ve done this, and set the properties of the new controls as I describe below, you should have a form
that looks something like this:

The properties of the new controls that we need to modify areTTableDatabaseName

BCDEMOS

Name

DetailTable

TableName

events.dbTDataSourceDataSet

DetailTableNameDetailSource

TDBNavigator
DataSource

DetailSourceName

DetailNavigatorTDBGrid

DataSource

DetailSourceName

DetailGrid

shapeType75fFlipH0fFlipV0pib3a41e3f8cee96ea766f71cdc774aa4befLine0
3a41e3f8cee96ea766f71cdc774aa4be

Now link all the event handlers we defined for MasterTable and MasterSource to the same events of DetailTable and
DetailSource. You can do this by selecting the appropriate method from the drop down list for each event. For example, click on
the edit button of the AfterCancel event handler. In the list, you’ll see “AfterCancel”. Select it. That’s all you need do to share
the same event handler between the two TTAble components, and is the reason we took the trouble to use generic names when we
defined MasterTable’s event handlers.

The methods that are displayed in the dropdown list for each event handler are those methods in the current TForm that have the
same parameter list as handler associated with the event. For example, the AfterCancel event handler has a single parameter of
type TObject*. You’ll find that the dropdown list contains all the methods that have already been defined in TForm1 that take a
single parameter of type TObject*.

Now compile and run the modified program. You should be able to scroll through the two tables independently, add, delete and
modify records in each, and see the events for both tables appear in EventsListBox as they are triggered.

The events table has a field called VenueNo which identifies the venue at which the event takes place – the venue is the Venue in
the venues table with the same value of VenueNo. It’s easy to link VCL tables in a master-detail relationship: you simply define
appropriate values for the MasterSource, MasterFields and IndexName or IndexFieldNames properties of the detail table. The
VCL will even do most of the work for you.Start by setting the MasterSource property of DetailTable to MasterSource. Now
select the MasterFields property of DetailTable in the Object Inspector. You’ll see a small edit button on the right-hand side of
the value field. Click it. You should see the following dialog.

shapeType75fFlipH0fFlipV0pibe4d091b12ba7def49aaa616cc21be500fLine0
e4d091b12ba7def49aaa616cc21be500

Select VenueNo in the Available Indexes combobox. Then select the VenueNo entry in both the Detail Fields and Master Fields
listboxes. This will enable the Add button. Click it. This places the string “VenueNo -> VenueNo” in the Joined fields list. Now
click the OK button. Having done this, you’ll find that the DetailTable’s IndexName and MasterFields properties have both been
set to VenueNo.

That’s all there is to it. You’ve created a master-detail relationship, based on the VenueNo field, between the venues and events
tables.

Now, when you run the program, you’ll find that the records displayed in the Detail table are limited to those that will take place
at the venue currently selected in the Master table. Look at what happens when you scroll through the Master table:

MasterTable: BeforeScrollDetailTable: BeforeScrollDetailSource: OnDataChange [NULL]
DetailTable: AfterScroll
MasterSource: OnDataChange [NULL]
MasterTable: AfterScroll
MasterNavigator: nbNext

The MasterTable’s BeforeScroll event fires, but then the DetailTable completes all its activities before the MasterTable's data
changes.

I wasn’t expecting this, and is something I learnt whilst writing this tutorial. It seems to be another case of the VCL assigning
priorities to events which are the opposite to the order in which they are actually triggered (as in the MasterNavigator click being
picked up after all the consequential database changes).

Now look at the activity that takes place when you try to insert a record in the venues table:

background image

MasterTable: BeforeInsertMasterTable: BeforeScrollMasterSource: InsertMasterTable: OnNewRecordDetailTable:
BeforeScrollDetailSource: OnDataChange [NULL]
DetailTable: AfterScroll
MasterSource: OnDataChange [NULL]
MasterTable: AfterInsert
MasterTable: AfterScroll
MasterNavigator: nbInsert

All of this happens as a result of clicking on the Insert button on the MasterNavigator. When you insert a new record into a table,
the new record becomes the current record. When the table is the master table in a master-detail relationship, this triggers the
corresponding changes in the detail table: it has to scroll through to the records (if any) which correspond to null value(s) in the
key(s) that define the master-detail relationship.

However, when you edit one of the records in the detail table, the MasterTable remains unaffected. Change the venue number of
one of the “homeless” events currently displayed in the detail table to, say, 3.

DetailTable: BeforeEditDetailSource: EditDetailSource: OnDataChange [NULL]DetailTable:

AfterEditDetailSource: OnDataChange [VenueNo]DetailSource: OnUpdateDataDetailTable: BeforePost
DetailSource: Browse
DetailSource: OnDataChange [NULL]
DetailTable: AfterPost

Notice that the record that has been given a home has disappeared from the DetailGrid.

7

And finally…

That’s the end of this tutorial. Play around with the program you now have: it can reveal a great deal about how BCB handles
database events. If you run into a problem with your “real” database programs, it might be worthwhile to come back to this one
to see if it sheds any light on what’s going on.

A Final Note

No liability is accepted by the autor(s) for anything which may occur whilst following this tutorial.

Contacts:

Main Site: forgot@mcmail.com
Newspages: bitsnews@mcmail.com
Comments: comments@mcmail.com

Section Editors:

Tutorials : Simon Rutley-Frayne, ccc@eclipse.co.uk
Components : Jon Jenkinson, jon.jenkinson@mcmail.com
Others bits: Will Green, pha97wg@sheffield.ac.uk

Author:

If you have a comment on the tutorial, contact me on John Kirkpatrick at john@isc-ltd.co.uk

Legal Stuff

This document is Copyright © Simon Rutley-Frayne, December 1998
The PDF distribution release of this document is Copyright ©"The Bits...", December 1998

In plain words, if you wish to do anything other than read and print this document for you own individual
use, let us know so we can track what's going on:)

*However* in more complicated speak,

You May,

Redistribute this file FREE OF CHARGE

providing

a)

you inform **Both** the author and "The Bits..." Website. *Initial contact forgot@mcmail.com*

b)

you do not charge recipients anything for the process of redistribution.

c)

You do not charge for the document itself.

background image

d)

You acknowledge both the author and the site location so that people can find the
document's origin for any updates.

We've found that we get mails from users who have read old copies of the documents, and come across
errors we've corrected. If we know as many distribution points as possible, we can keep you and thus your
visitors up-to-date.

You Must Gain written Permission, (we'll email you:),

If you choose to redistribute this document in any way,(book/CD/Magazine/Web Site etc.).

Note, this is to avoid any possible conflict with commercial work the author may be undertaking, but
it is unlikely permission will be refused. *Initial contact forgot@mcmail.com*

Note: If you choose to distribute the document be aware that neither "The Bits..." nor the author
will be held liable for anything that occurs whilst this document is being used.

You Must Gain written Permission, (we'll email you:)

If you wish to use all or part of this document for commercial training purposes.
*Initial contact forgot@mcmail.com*
Note: No liability, commercial, public or other will be accepted by "The Bits..." or the author
for anything which may occur should you choose to use this document as part of a commercial
training document.

You Cannot,

Under any circumstances alter this document in any way without the express permission of the
author or "The Bits...". Contact us and we'll look into your suggestions. If we agree with what you
say, we will include you as a co-author of the document.

You Cannot,

Receive **any** monies for this document without the prior consent of the author.

You Cannot,

Take any of the ideas presented here and produce a product for distribution without the express
written consent of the author. However, you may take what you learn here and produce a
commercial product based on your own interpretation of your newly gained knowledge.


Wyszukiwarka

Podobne podstrony:
c++ builder 5 cwiczenia praktyczne UBS5IHHM4X72DJVSTUEPJ6N45C7DLODWSDYH3KQ
checklist radio tv theatre events
C++Builder 6 Ćwiczenia
borland cpp builder cw10
borland cpp builder cw13
borland cpp builder cw9
programowanie w delphi i c++ builder ii czesc MAYYMABSRUI5UEU3TMO5XUFRDPRBWZNIJRHDQIA
C++ Builder, REGISTRY
C Builder Kompendium programisty cbu6kp
Informacje o C++ Builder, Programowanie, C++ Builder
R14-03, ## Documents ##, C++Builder 5
R17-03, ## Documents ##, C++Builder 5
The American Civil War and the Events that led to its End
borland cpp builder cw2
programuje w delphi i c builder (2)
Ebook Borland C++ Builder 5?velopers Guide (1)
29 PE Events id 32202 Nieznany (2)

więcej podobnych podstron