Skip to content

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 dataset
  • TInstantSelector - 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: TDataSetTInstantCustomExposerTInstantExposer

Key Properties

PropertyTypeDescription
SubjectTInstantObjectObject being exposed
ObjectClassTInstantObjectClassClass of objects to expose
ContainerNamestringContainer attribute name for master-detail
MasterSourceTDataSourceMaster datasource for detail relationship
OptionsTInstantExposerOptionsBehavior options
ModeTInstantExposerModeamContent or amBrowse
OnIncludeFieldTInstantIncludeFieldEventFilter which fields to include

TInstantExposerOptions

pascal
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

pascal
// 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

pascal
// 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

pascal
// 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 collection

Advanced Usage - Custom Fields

pascal
// 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

pascal
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: TDataSetTInstantCustomExposerTInstantSelector

Key Properties

PropertyTypeDescription
CommandTInstantCommandIQL query or object specification
ConnectorTInstantConnectorDatabase connector
ObjectClassTInstantObjectClassClass of objects to select
OptionsTInstantExposerOptionsBehavior options (same as Exposer)
OnCompareTInstantCompareObjectsEventCustom sorting
OnFilterObjectTInstantFilterObjectEventClient-side filtering
OnLimitTInstantLimitEventLimit result set

Key Methods

pascal
// 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

pascal
// 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

pascal
// Display specific objects
ContactSelector.ObjectClass := TContact;
ContactSelector.Open;

// Add objects manually
ContactSelector.AddObject(Contact1);
ContactSelector.AddObject(Contact2);

Sorting

pascal
// 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

pascal
// 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

pascal
// 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

pascal
// 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

pascal
// 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 TypeField TypeNotes
TInstantStringTStringFieldSize from metadata
TInstantIntegerTIntegerField (or TLargeIntField)
TInstantFloatTFloatField
TInstantCurrencyTCurrencyField
TInstantBooleanTBooleanField
TInstantDateTimeTDateTimeField
TInstantDateTDateField
TInstantTimeTTimeField
TInstantBlobTBlobField
TInstantMemoTMemoField (or TWideMemoField)
TInstantGraphicTGraphicField
TInstantReferenceTStringField (ref), nested fieldsShows referenced ID
TInstantPartNested object fieldsEmbedded as fields
TInstantPartsNot directly shownUse detail Exposer
TInstantReferencesNot directly shownUse detail Exposer

Common Patterns

Browse and Edit Pattern

pascal
// 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;
pascal
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

  1. Close before changing query - Call Close before modifying Command.Text
  2. Use eoAutoApply for simple forms - Automatic persistence
  3. Disable eoAutoApply for browse grids - Explicit save control
  4. Use Refresh, not Reopen - Refresh is faster for updates
  5. Free objects appropriately - Selector owns objects unless removed
  6. Handle OnBeforePost - Validate before saving
  7. Use try-finally - Ensure proper cleanup
  8. Limit large result sets - Use OnLimit or TOP clause
  9. 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

Released under Mozilla License, Version 2.0.