Table of Contents

Database adapter

TTMSFNCPlannerDatabaseAdapter connects a planner to any TDataSet through a TDataSource, so appointments are loaded from and saved to a database instead of created in code. You map each planner item field to a dataset field once, activate the adapter, and the planner reads, inserts, updates, and deletes items as the user interacts with it. Reach for the adapter whenever the schedule must persist between sessions or be shared across machines. This guide covers field mapping, activation, reloading on navigation, and the customization events.

Field mapping

The adapter's Item sub-object holds one string property per planner field; set each to the name of the dataset field that supplies it. AutoIncrementDBKey lets the adapter generate keys for new rows:

procedure TForm1.BindPlannerToDataset;
begin
  // Map each planner item field to a dataset field name.
  PlannerDatabaseAdapter1.Item.DataSource := DataSource1;
  PlannerDatabaseAdapter1.Item.DBKey := 'Id';
  PlannerDatabaseAdapter1.Item.StartTime := 'StartTime';
  PlannerDatabaseAdapter1.Item.EndTime := 'EndTime';
  PlannerDatabaseAdapter1.Item.Title := 'Title';
  PlannerDatabaseAdapter1.Item.Text := 'Notes';
  PlannerDatabaseAdapter1.Item.Resource := 'Resource';
  PlannerDatabaseAdapter1.Item.Recurrency := 'Recurrency';
  PlannerDatabaseAdapter1.Item.AutoIncrementDBKey := True;

  // Connect the adapter to the planner and activate it.
  Planner1.Adapter := PlannerDatabaseAdapter1;
  PlannerDatabaseAdapter1.Active := True;

  // Reload the visible window whenever the user navigates.
  Planner1.OnAfterNavigateToDateTime := PlannerAfterNavigate;
end;

procedure TForm1.PlannerAfterNavigate(Sender: TObject;
  ADirection: TTMSFNCPlannerNavigationDirection;
  ACurrentDateTime: TDateTime; ANewDateTime: TDateTime);
begin
  PlannerDatabaseAdapter1.GetItems(Planner1.TimeLine.ViewStart,
                                   Planner1.TimeLine.ViewStart + 7);
end;

Assign the adapter to Planner1.Adapter and set Adapter.Active := True to load the data. The Recurrency mapping is optional — map it only when the dataset stores RRULE strings.

Loading a time window

GetItems(PeriodFrom, PeriodTo) loads the items for a date range; LoadItems reloads everything. Because a planner only shows one window at a time, reload on the OnAfterNavigateToDateTime event (wired in the snippet above) so moving to the next week fetches that week's rows. OnItemsLoaded fires after a load completes — use it to update a status indicator:

procedure TForm1.LoadVisibleWindow;
begin
  // Load only the items in the current view (here a seven-day window).
  PlannerDatabaseAdapter1.GetItems(Planner1.TimeLine.ViewStart,
                                   Planner1.TimeLine.ViewStart + 7);
end;

procedure TForm1.ReloadEverything;
begin
  PlannerDatabaseAdapter1.LoadItems;
end;

procedure TForm1.PlannerItemsLoaded(Sender: TObject);
begin
  // OnItemsLoaded fires after a load completes.
  Caption := Format('%d appointments loaded', [Planner1.Items.Count]);
end;

Customizing reads and writes

Two events let you translate between item and dataset when the default field mapping is not enough:

  • OnFieldsToItem — populate a TTMSFNCPlannerItem from the current dataset row (e.g. derive Color from a status field).
  • OnItemToFields — write a TTMSFNCPlannerItem back into dataset fields before post (e.g. split a combined field).

OnItemInserted, OnItemUpdated, and OnItemRead report each persisted change, and OnItemCreateDBKey supplies a custom key when AutoIncrementDBKey is off.

Putting it together

This example combines field mapping with a read customization: items are colored from a Status column as each row is read into the planner, so the data binding and the appearance are driven entirely by the dataset:

procedure TForm1.BindWithStatusColors;
begin
  // Standard field mapping.
  PlannerDatabaseAdapter1.Item.DataSource := DataSource1;
  PlannerDatabaseAdapter1.Item.DBKey := 'Id';
  PlannerDatabaseAdapter1.Item.StartTime := 'StartTime';
  PlannerDatabaseAdapter1.Item.EndTime := 'EndTime';
  PlannerDatabaseAdapter1.Item.Title := 'Title';

  // Customize each read: color the item from a status column.
  PlannerDatabaseAdapter1.OnFieldsToItem := AdapterFieldsToItem;

  Planner1.Adapter := PlannerDatabaseAdapter1;
  PlannerDatabaseAdapter1.Active := True;
end;

procedure TForm1.AdapterFieldsToItem(Sender: TObject; AFields: TFields;
  AItem: TTMSFNCPlannerItem);
begin
  if AFields.FindField('Status') <> nil then
  begin
    if AFields.FieldByName('Status').AsString = 'Urgent' then
      AItem.Color := gcRed
    else
      AItem.Color := gcCornflowerblue;
    AItem.FontColor := gcWhite;
  end;
end;

Pitfalls

  • Map DBKey for updates and deletes. Without a DBKey mapping the adapter cannot locate the row behind an item, so edits and deletes will not persist.
  • Activate after mapping. Set the field names first, then Adapter.Active := True; activating against an unmapped adapter loads nothing.
  • Reload on navigation. The adapter loads the current window; if you do not call GetItems from OnAfterNavigateToDateTime, navigating shows an empty period.
  • AutoIncrementDBKey vs. database keys. Turn it off and handle OnItemCreateDBKey when the database (not the adapter) assigns primary keys.

See also

  • Items — the item fields the adapter maps
  • Recurrence — persisting recurring items via the RRULE field
  • Timeline — the navigation that drives reloads