Recurrency editor
TTMSFNCPlannerItemEditorRecurrency extends the built-in planner item editor with a recurrency panel. When connected to TTMSFNCPlanner.ItemEditor, the editing dialog gains two extra tabs — General (title, text, start/end time, full-day flag) and Recurrency (frequency, interval, day-of-week, range, and exception dates) — so users can define repeating events without any additional code.
Connecting the component
Drop a TTMSFNCPlannerItemEditorRecurrency on the form and assign it to the planner's ItemEditor property at design time or at runtime:
// Drop a TTMSFNCPlannerItemEditorRecurrency on the form and connect it
procedure TForm1.FormCreate(Sender: TObject);
begin
// Link the recurrency editor to the planner's item editor slot
TMSFNCPlanner1.ItemEditor := TMSFNCPlannerItemEditorRecurrency1;
// The built-in editing dialog now shows General and Recurrency tabs
// whenever the user double-clicks an item or uses keyboard editing
end;
After the assignment, double-clicking an item (or using the keyboard shortcut when Interaction.EditingMode is emDialog) opens the extended dialog automatically.
Recurrency frequency
The component supports six frequencies, represented by TTMSFNCRecurrencyFrequency:
| Value | Meaning |
|---|---|
rfHourly |
Repeats every N hours |
rfDaily |
Repeats every N days |
rfWeekly |
Repeats on selected days of the week every N weeks |
rfMonthly |
Repeats on a day of the month every N months |
rfYearly |
Repeats on a specific date every N years |
rfNone |
One-time event (no recurrency) |
The user selects the frequency and interval in the Recurrency tab. The range can be defined as a fixed number of occurrences or by an end date.
Storing the recurrency string
The recurrency pattern is serialised as a compact string (an iCalendar-style RRULE). When using TTMSFNCPlannerDatabaseAdapter, map this string to a database field using the adapter's Item.Recurrency property:
// Wire the database adapter and map the recurrency field
procedure TForm1.FormCreate(Sender: TObject);
begin
TMSFNCPlanner1.Adapter := TMSFNCPlannerDatabaseAdapter1;
TMSFNCPlanner1.ItemEditor := TMSFNCPlannerItemEditorRecurrency1;
with TMSFNCPlannerDatabaseAdapter1.Item do
begin
DataSource := DataSource1;
DBKey := 'Id';
StartTime := 'StartTime';
EndTime := 'EndTime';
Title := 'Title';
Text := 'Text';
Resource := 'Resource';
Recurrency := 'Recurrency'; // field that stores the RRULE string
end;
// Load initial items from the dataset
TMSFNCPlannerDatabaseAdapter1.LoadItems;
end;
The adapter reads and writes the recurrency string automatically when items are loaded or saved. Exception dates (days excluded from the pattern) are also stored in the same field.
Working with recurrency strings programmatically
Use TTMSFNCRecurrencyHandler to parse an existing recurrency string, iterate over the generated dates, or compose a new RRULE string without the editor UI:
// Parse and inspect a recurrency string using TTMSFNCRecurrencyHandler
procedure TForm1.InspectRecurrency(const ARRule: string);
var
Handler: TTMSFNCRecurrencyHandler;
StartDate, EndDate: TDateTime;
begin
Handler := TTMSFNCRecurrencyHandler.Create;
try
Handler.Recurrency := ARRule;
Handler.StartTime := EncodeDate(2025, 1, 6); // Monday
Handler.EndTime := EncodeDate(2025, 1, 6);
Handler.Parse;
if Handler.IsRecurrent then
begin
Memo1.Lines.Add('Frequency : ' + IntToStr(Ord(Handler.Frequency)));
Memo1.Lines.Add('Interval : ' + IntToStr(Handler.Interval));
Memo1.Lines.Add('Occurrences:');
StartDate := 0;
EndDate := 0;
while Handler.NextDate(StartDate, EndDate) do
Memo1.Lines.Add(DateToStr(StartDate));
end;
finally
Handler.Free;
end;
end;
// Build a recurrency string programmatically
procedure TForm1.BuildWeeklyRRule;
var
Handler: TTMSFNCRecurrencyHandler;
begin
Handler := TTMSFNCRecurrencyHandler.Create;
try
Handler.Frequency := rfWeekly;
Handler.Interval := 1;
Handler.RepeatCount := 10; // 10 occurrences
Handler.Days := [2, 4]; // Tuesday = 2, Thursday = 4 (1-based)
Handler.StartTime := EncodeDate(2025, 1, 6);
Handler.EndTime := EncodeDate(2025, 1, 6);
Handler.Generate;
ShowMessage(Handler.Compose);
finally
Handler.Free;
end;
end;
Key TTMSFNCRecurrencyHandler members:
| Member | Type | Description |
|---|---|---|
Recurrency |
string |
The RRULE string to parse or the output of Compose |
Frequency |
TTMSFNCRecurrencyFrequency |
Pattern frequency |
Interval |
Integer |
Number of units between repetitions |
RepeatCount |
Integer |
Maximum number of occurrences (0 = use RepeatUntil) |
RepeatUntil |
TDateTime |
End date for the recurrence series |
Days |
TTMSFNCRecurrencyHandlerDaySet |
Set of week-day bytes (1 = Sunday … 7 = Saturday) |
ExDates |
TTMSFNCRecurrencyDateItems |
Exception date ranges excluded from the pattern |
Parse |
method | Parses Recurrency into the structured fields |
Generate |
method | Populates Dates from the structured fields |
Compose |
method | Serialises the structured fields back to a string |
IsRecurrent |
method | Returns True when Recurrency encodes a repeating pattern |
NextDate |
method | Advances to the next start/end date pair; returns False when exhausted |
Getting and setting the recurrency string on the editor directly
If you need to pre-populate the editor or read back the current pattern without going through a database adapter, call SetRecurrency and GetRecurrency on the component:
// Pre-populate for a weekly event every Monday
TMSFNCPlannerItemEditorRecurrency1.SetRecurrency(
'RRULE:FREQ=WEEKLY;INTERVAL=1;BYDAY=MO');
// Read the pattern the user configured
var
RRule: string;
begin
RRule := TMSFNCPlannerItemEditorRecurrency1.GetRecurrency;
end;
Combining the editor, programmatic strings, and a recurrency handler
Pre-populate the editor with a known RRULE, let the user adjust it, then iterate the resulting dates using TTMSFNCRecurrencyHandler:
procedure TForm1.FormCreate(Sender: TObject);
begin
TMSFNCPlannerItemEditorRecurrency1.SetRecurrency(
'RRULE:FREQ=WEEKLY;INTERVAL=1;BYDAY=MO,WE,FR');
TMSFNCPlanner1.ItemEditor := TMSFNCPlannerItemEditorRecurrency1;
end;
procedure TForm1.ButtonPreviewClick(Sender: TObject);
var
h: TTMSFNCRecurrencyHandler;
startDT, endDT: TDateTime;
begin
h := TTMSFNCRecurrencyHandler.Create;
try
h.Recurrency := TMSFNCPlannerItemEditorRecurrency1.GetRecurrency;
h.Parse;
h.Generate;
while h.NextDate(startDT, endDT) do
Memo1.Lines.Add(DateTimeToStr(startDT));
finally
h.Free;
end;
end;