InstantPresentation
Unit: InstantPresentationCategory: Presentation
Overview
The InstantPresentation unit provides data-aware components that connect InstantObjects to standard VCL/FMX controls. These components act as TDataSet descendants, allowing seamless integration with DBGrid, DBEdit, and other data-aware controls.
Key Components:
TInstantExposer- Exposes a single object as a datasetTInstantSelector- Exposes a collection of objects as a dataset
TInstantExposer
Exposes properties of a single TInstantObject instance as dataset fields, enabling editing with standard data-aware controls.
Inheritance: TDataSet → TInstantCustomExposer → TInstantExposer
Key Properties
| Property | Type | Description |
|---|---|---|
Subject | TInstantObject | Object being exposed |
ObjectClass | TInstantObjectClass | Class of objects to expose |
ContainerName | string | Container attribute name for master-detail |
MasterSource | TDataSource | Master datasource for detail relationship |
Options | TInstantExposerOptions | Behavior options |
Mode | TInstantExposerMode | amContent or amBrowse |
OnIncludeField | TInstantIncludeFieldEvent | Filter which fields to include |
TInstantExposerOptions
TInstantExposerOptions = set of TInstantExposerOption;
TInstantExposerOption = (
eoAutoApply, // Auto-save changes on Post
eoDeferInsert, // Defer insert until Post (not on Insert/Append)
eoAutoRemember, // Auto-remember state on Edit
eoNotDisposeReferences // Don't dispose referenced objects on delete
);Key Methods
// Object Management
procedure AssignSubject(AObject: TInstantObject);
procedure ApplyChanges;
procedure CancelChanges;
procedure RefreshCurrentObject;
// Field Management
procedure AddFieldDef(const AName: string; ADataType: TFieldType; ASize: Integer);
procedure FieldByName(const AName: string): TField;Basic Usage
// Single object editing
var
Contact: TContact;
begin
Contact := TContact.Retrieve('CONT001');
ContactExposer.ObjectClass := TContact;
ContactExposer.Subject := Contact;
ContactExposer.Options := [eoAutoApply]; // Auto-save on Post
// Now DBEdits bound to ContactExposer.DataSource show Contact fields
ContactExposer.Open;
// Edit through data-aware controls...
// Changes are automatically saved when eoAutoApply is set
end;Master-Detail Relationships
// Master: Contact
ContactExposer.ObjectClass := TContact;
ContactExposer.Subject := CurrentContact;
ContactExposer.Open;
// Detail: Phones (Parts collection)
PhoneExposer.ObjectClass := TPhone;
PhoneExposer.ContainerName := 'Phones'; // Name of TInstantParts attribute
PhoneExposer.MasterSource := ContactDataSource;
PhoneExposer.Open;
// PhoneExposer now shows phones of current contact
// Adding/deleting in grid affects Contact.Phones collectionAdvanced Usage - Custom Fields
// Add calculated or custom fields
procedure TMainForm.ContactExposerAfterOpen(DataSet: TDataSet);
begin
with ContactExposer do
begin
// Add calculated field
FieldDefs.Add('FullName', ftString, 100);
// Add lookup field
FieldDefs.Add('CategoryName', ftString, 50);
end;
end;
// Calculate field value
procedure TMainForm.ContactExposerCalcFields(DataSet: TDataSet);
var
Contact: TContact;
begin
Contact := ContactExposer.CurrentObject as TContact;
if Assigned(Contact) then
begin
ContactExposer.FieldByName('FullName').AsString :=
Contact.FirstName + ' ' + Contact.LastName;
if Assigned(Contact.Category) then
ContactExposer.FieldByName('CategoryName').AsString :=
Contact.Category.Name;
end;
end;Events
OnFilterRecord: TFilterRecordEvent;
OnNewRecord: TDataSetNotifyEvent;
OnBeforePost: TDataSetNotifyEvent;
OnAfterPost: TDataSetNotifyEvent;
OnBeforeDelete: TDataSetNotifyEvent;
OnAfterDelete: TDataSetNotifyEvent;TInstantSelector
Exposes a collection of TInstantObject instances as a dataset, typically used with grids to display and edit multiple objects.
Inheritance: TDataSet → TInstantCustomExposer → TInstantSelector
Key Properties
| Property | Type | Description |
|---|---|---|
Command | TInstantCommand | IQL query or object specification |
Connector | TInstantConnector | Database connector |
ObjectClass | TInstantObjectClass | Class of objects to select |
Options | TInstantExposerOptions | Behavior options (same as Exposer) |
OnCompare | TInstantCompareObjectsEvent | Custom sorting |
OnFilterObject | TInstantFilterObjectEvent | Client-side filtering |
OnLimit | TInstantLimitEvent | Limit result set |
Key Methods
// Query Execution
procedure Open;
procedure Refresh;
// Object Access
function CurrentObject: TInstantObject;
function ObjectAtRow(Row: Integer): TInstantObject;
// Object Manipulation
function AddObject(AObject: TInstantObject): Integer;
function InsertObject(Index: Integer; AObject: TInstantObject): Integer;
procedure RemoveObject(AObject: TInstantObject);
function IndexOfObject(AObject: TInstantObject): Integer;
// Navigation
procedure GotoObject(AObject: TInstantObject);Basic Usage - IQL Query
// Display all contacts
ContactSelector.Command.Text := 'SELECT * FROM TContact';
ContactSelector.Open;
// Display filtered contacts
ContactSelector.Command.Text :=
'SELECT * FROM TContact WHERE Category.Name = ''Customer'' ORDER BY Name';
ContactSelector.Open;
// Parameterized query
ContactSelector.Command.Text :=
'SELECT * FROM TContact WHERE Category.Name = :CategoryName';
ContactSelector.Command.ParamByName('CategoryName').AsString := 'Customer';
ContactSelector.Open;Basic Usage - Object List
// Display specific objects
ContactSelector.ObjectClass := TContact;
ContactSelector.Open;
// Add objects manually
ContactSelector.AddObject(Contact1);
ContactSelector.AddObject(Contact2);Sorting
// Sort by field
ContactSelector.Command.Text :=
'SELECT * FROM TContact ORDER BY Name';
// Custom sort using OnCompare
procedure TMainForm.ContactSelectorCompare(Sender, AObject1, AObject2: TObject;
var Compare: Integer);
var
C1, C2: TContact;
begin
C1 := AObject1 as TContact;
C2 := AObject2 as TContact;
// Custom comparison logic
Compare := CompareText(C1.LastName, C2.LastName);
if Compare = 0 then
Compare := CompareText(C1.FirstName, C2.FirstName);
end;Filtering
// IQL filtering
ContactSelector.Command.Text :=
'SELECT * FROM TContact WHERE Active = True';
// Client-side filtering using OnFilterObject
procedure TMainForm.ContactSelectorFilterObject(Sender: TObject;
AObject: TInstantObject; var Accept: Boolean);
var
Contact: TContact;
begin
Contact := AObject as TContact;
Accept := (Contact.Country = 'USA') and (Contact.Sales > 1000);
end;
// Enable filtering
ContactSelector.Filtered := True;Adding and Editing Objects
// Add new object via grid
procedure TMainForm.AddButtonClick(Sender: TObject);
var
Contact: TContact;
begin
Contact := TContact.Create;
Contact.Id := InstantGenerateId;
ContactSelector.AddObject(Contact);
// Grid shows new empty row
// User fills in data via DBEdits
// Post saves if eoAutoApply is set
end;
// Delete object
procedure TMainForm.DeleteButtonClick(Sender: TObject);
begin
if Assigned(ContactSelector.CurrentObject) then
begin
ContactSelector.Delete; // Removes from selector and disposes object
end;
end;Performance - Limiting Results
// Limit using OnLimit event
procedure TMainForm.ContactSelectorLimit(Sender, AObject: TObject;
var Accept: Boolean);
begin
// Only load first 100 objects
Accept := ContactSelector.ObjectCount < 100;
end;
// Or use IQL with TOP clause (broker-specific)
ContactSelector.Command.Text :=
'SELECT FIRST 100 * FROM TContact ORDER BY CreatedAt DESC';Master-Detail with Selector
// Master selector
CompanySelector.Command.Text := 'SELECT * FROM TCompany';
CompanySelector.Open;
// Detail selector showing employees
EmployeeSelector.Command.Text :=
'SELECT * FROM TPerson WHERE Company.Id = :CompanyId';
EmployeeSelector.MasterSource := CompanyDataSource;
EmployeeSelector.MasterParamFieldName := 'Id'; // Field to link
EmployeeSelector.Open;
// Or using reference syntax
EmployeeSelector.Command.Text :=
'SELECT * FROM TPerson WHERE Company = :Company';
EmployeeSelector.MasterSource := CompanyDataSource;Field Mapping
Both Exposer and Selector automatically create dataset fields from object attributes:
| Attribute Type | Field Type | Notes |
|---|---|---|
| TInstantString | TStringField | Size from metadata |
| TInstantInteger | TIntegerField (or TLargeIntField) | |
| TInstantFloat | TFloatField | |
| TInstantCurrency | TCurrencyField | |
| TInstantBoolean | TBooleanField | |
| TInstantDateTime | TDateTimeField | |
| TInstantDate | TDateField | |
| TInstantTime | TTimeField | |
| TInstantBlob | TBlobField | |
| TInstantMemo | TMemoField (or TWideMemoField) | |
| TInstantGraphic | TGraphicField | |
| TInstantReference | TStringField (ref), nested fields | Shows referenced ID |
| TInstantPart | Nested object fields | Embedded as fields |
| TInstantParts | Not directly shown | Use detail Exposer |
| TInstantReferences | Not directly shown | Use detail Exposer |
Common Patterns
Browse and Edit Pattern
// Browse form with grid
procedure TBrowseForm.FormCreate(Sender: TObject);
begin
ContactSelector.Command.Text := 'SELECT * FROM TContact ORDER BY Name';
ContactSelector.Options := []; // No auto-apply in browse
ContactSelector.Open;
end;
// Edit selected contact
procedure TBrowseForm.EditButtonClick(Sender: TObject);
var
EditForm: TEditForm;
Contact: TContact;
begin
Contact := ContactSelector.CurrentObject as TContact;
if Assigned(Contact) then
begin
EditForm := TEditForm.Create(Self);
try
EditForm.EditContact(Contact);
if EditForm.ShowModal = mrOk then
ContactSelector.Refresh; // Refresh grid
finally
EditForm.Free;
end;
end;
end;
// Edit form with exposer
procedure TEditForm.EditContact(AContact: TContact);
begin
ContactExposer.Subject := AContact;
ContactExposer.Options := [eoAutoApply];
ContactExposer.Open;
end;Incremental Search
procedure TBrowseForm.SearchEditChange(Sender: TObject);
var
SearchText: string;
begin
SearchText := SearchEdit.Text;
if SearchText <> '' then
begin
ContactSelector.Command.Text :=
Format('SELECT * FROM TContact WHERE Name LIKE ''%s%%''',
[SearchText]);
ContactSelector.Open;
end;
end;Best Practices
- Close before changing query - Call
Closebefore modifyingCommand.Text - Use eoAutoApply for simple forms - Automatic persistence
- Disable eoAutoApply for browse grids - Explicit save control
- Use Refresh, not Reopen -
Refreshis faster for updates - Free objects appropriately - Selector owns objects unless removed
- Handle OnBeforePost - Validate before saving
- Use try-finally - Ensure proper cleanup
- Limit large result sets - Use OnLimit or TOP clause
- Index sort fields - Improve query performance
Troubleshooting
Problem: Grid doesn't show all fields
- Solution: Check ObjectClass is set correctly and object has those attributes
Problem: Changes not saving
- Solution: Ensure eoAutoApply is set or call ApplyChanges explicitly
Problem: AV when editing
- Solution: Verify Subject is assigned before opening Exposer
Problem: Master-detail not working
- Solution: Check MasterSource is set and ContainerName matches attribute name
Problem: Slow query performance
- Solution: Add indexes on filter/sort fields, use OnLimit to restrict rows
See Also
- InstantPersistence - Base object classes
- InstantCommand - IQL query syntax
- User Guide - Exposing Objects - Exposer tutorial
- User Guide - Selecting Objects - Selector tutorial
- Primer Demo - Working examples
