Blog
All Blog Posts | Next Post | Previous Post
Filtering in Delphi: Building a Highlighting Property-Based Search
Monday, September 7, 2026
In the previous blog post, we introduced the new TTMSFNCFilterRulesManager and looked at how filtering can go beyond datasets and grid columns.
Instead of only filtering data, the Filter Rules Manager can evaluate properties of Delphi objects and use the result to change other properties or execute application logic.
We also showed the following example:

In this blog post, we're going to take a closer look at that example and see how it was built.
The application contains a TTMSFNCPlanner with several appointments and a search box at the top.
As the user types, appointments with a matching title keep their original appearance, while appointments that don't match are dimmed.
There is no dataset filter involved.
Instead, we're going to filter directly on the Title property of each TTMSFNCPlannerItem.
Setting Up the Planner
For this example, the Planner contains a number of appointments spread across the working week.
Adding an appointment is nothing special:
Item := TMSFNCPlanner1.Items.Add; Item.Title := ATitle; Item.Text := AText; Item.StartTime := AStartTime; Item.EndTime := AEndTime; Item.Color := AFill;
Some examples used in the demo are:
- Team Standup
- Sprint Planning
- Architecture Review
- Design Review
- Product Demo
- Training Session
- Sprint Retrospective
What matters for our rule is that every appointment already has a Title property.
That's the property we're going to search.
Creating the Filter Rules Manager
We start by creating a TTMSFNCFilterRulesManager:
FRM := TTMSFNCFilterRulesManager.Create(Self);
Next, we need to connect the text entered by the user to our rule.
We could recreate the filter expression every time the user types something, but there is a better option: placeholders.
Adding a Dynamic Search Value
A placeholder lets a rule retrieve a value from another object when the rule is evaluated.
Our search text comes from a TEdit called SearchEdit.
We register its Text property as a placeholder:
FRM.AddPlaceholder( 'Search', SearchEdit, 'Text' );
From this point on, we can use:
{Search}inside our rule.
Whenever the rule is applied, {Search} is replaced with the current value of SearchEdit.Text.
This is an important detail: the value isn't fixed when the rule is created.
The same rule can therefore remain in place while the user continues typing.
Creating the Rule
Now we can create the actual rule:
FRule1 := FRM.AddRule(
'SpotlightRule',
TMSFNCPlanner1,
'Items',
'[Title] LIKE ''%{Search}%'''
);This small piece of code contains most of the logic for our search.
Let's break it down.
1. The Rule Name
'SpotlightRule'
Every rule has a name so it can be identified later.
2. The Source
TMSFNCPlanner1
This is the object where the Filter Rules Manager starts.
But we don't want to evaluate the Planner itself. We want to evaluate its appointments.
That's where the next parameter comes in.
3. The Items Path
'Items'
The Filter Rules Manager uses this path to retrieve the collection of objects that should be evaluated.
In this example, that means every TTMSFNCPlannerItem inside:
TMSFNCPlanner1.Items
The rule is therefore applied to each appointment individually.
4. The Filter Expression
Finally, we have the actual condition:
'[Title] LIKE ''%{Search}%'''This is where things become interesting.
Title is not a field from a dataset.
It is the Title property of TTMSFNCPlannerItem.
For every planner item, the Rules Manager retrieves that property and evaluates it using the filter expression.
If the user enters:
Team
the effective condition becomes:
[Title] LIKE '%Team%'
Appointments such as Team Standup match, while appointments such as Product Demo don't.
This is the key difference with the filtering examples from earlier in this series.
We're using the same structured filtering concepts, but now we're evaluating properties directly on Delphi objects.
What Should Happen When an Item Matches?
We now know whether each appointment matches the search.
The next question is what we want to do with that information.
We could hide appointments that don't match.
But for this example, we want to create more of a spotlight effect.
Matching appointments remain unchanged, while the other appointments are visually dimmed.
For that, we use CallBackAction:
FRule1.CallBackAction :=
procedure(
const ARule: TTMSFNCFilterRule;
AItem: TObject;
AIndex: Integer;
const AMatched: Boolean)
var
Item: TTMSFNCPlannerItem;
Idx: Integer;
begin
Item := AItem as TTMSFNCPlannerItem;
Idx := Item.Index;
if AMatched then
begin
Item.Color := FColors[Idx].Fill;
Item.StrokeColor := FColors[Idx].Stroke;
Item.FontColor := FColors[Idx].Font;
Item.TitleFontColor := FColors[Idx].Font;
Item.MarkColor := FColors[Idx].Mark;
Inc(FMatchCount);
end
else
begin
Item.Color := DimColor(FColors[Idx].Fill);
Item.StrokeColor := DimColor(FColors[Idx].Stroke);
Item.FontColor := DimColor(FColors[Idx].Font);
Item.TitleFontColor := DimColor(FColors[Idx].Font);
Item.MarkColor := DimColor(FColors[Idx].Mark);
end;
end;The callback is executed for every item processed by the rule.
The AMatched parameter immediately tells us whether the current planner item matched our Title condition.
When it matches, we restore its original colors.
When it doesn't, we apply a dimmed version of those colors.
One Match, Multiple Property Changes
This example also demonstrates another useful aspect of the Filter Rules Manager.
One condition can affect multiple properties.
For a non-matching appointment, we're changing:
ColorStrokeColorFontColorTitleFontColorMarkColor
All of these changes are driven by the result of a single condition:
[Title] LIKE '%{Search}%'For this example we use a callback because we want precise control over several appearance properties and we also keep track of the number of matching items.
For simpler cases, the Filter Rules Manager also provides actions that can directly update properties without requiring a callback.
Making the Search Live
We have created the rule only once.
To turn it into a live search, all we need to do is reapply it when the text changes.
procedure TFormFilterRules.SearchEditChangeTracking( Sender: TObject); begin UpdateFilter; end;
The UpdateFilter method itself remains very small:
procedure TFormFilterRules.UpdateFilter;
begin
FMatchCount := 0;
FRM.ApplyRule(FRule1);
CountLbl.Text := Format(
'%d / %d',
[FMatchCount, TMSFNCPlanner1.Items.Count]
);
end;Notice what isn't happening here.
We aren't:
- Iterating over the planner items ourselves.
- Reading every
Titleproperty manually. - Rebuilding the filter expression.
- Copying the current search value into the rule.
We simply apply the existing rule again.
The Rules Manager resolves {Search} from SearchEdit.Text, iterates over the Planner items, evaluates their Title properties and calls the appropriate action.
What Happens When the Rule Is Disabled?
Because our rule changes the appearance of the Planner items, we also need to consider what should happen when the rule itself becomes inactive.
For that, we can provide an InactiveCallBackAction.
FRule1.InactiveCallBackAction :=
procedure(
const ARule: TTMSFNCFilterRule;
AItem: TObject;
AIndex: Integer)
var
Item: TTMSFNCPlannerItem;
Idx: Integer;
begin
Item := AItem as TTMSFNCPlannerItem;
Idx := Item.Index;
Item.Color := FColors[Idx].Fill;
Item.StrokeColor := FColors[Idx].Stroke;
Item.FontColor := FColors[Idx].Font;
Item.TitleFontColor := FColors[Idx].Font;
Item.MarkColor := FColors[Idx].Mark;
end;When the rule is inactive, every appointment is restored to its original appearance.
This gives us three clear states:
- Match - keep the original appearance.
- No match - dim the appointment.
- Rule inactive - restore everything.
The Complete Rule Setup
If we remove the supporting UI and styling code, the actual rule setup is surprisingly compact:
FRM := TTMSFNCFilterRulesManager.Create(Self);
FRM.AddPlaceholder(
'Search',
SearchEdit,
'Text'
);
FRule1 := FRM.AddRule(
'SpotlightRule',
TMSFNCPlanner1,
'Items',
'[Title] LIKE ''%{Search}%'''
);
FRule1.CallBackAction :=
procedure(
const ARule: TTMSFNCFilterRule;
AItem: TObject;
AIndex: Integer;
const AMatched: Boolean)
begin
// Apply the matching or non-matching appearance here.
end;And when the search changes:
FRM.ApplyRule(FRule1);
That's the core of the example.
Not Just a Planner Feature
Although we're using TTMSFNCPlanner here, there is nothing Planner-specific about the rule itself.
The important ingredients are:
- An object that acts as the source.
- A path to the objects you want to evaluate.
- A property on those objects.
- A filter expression.
- An action to perform with the result.
In this example:
Source : TMSFNCPlanner1
Items : Items
Property : Title
Condition : LIKE '%{Search}%'
Action : Highlight matching appointmentsChange the source, item path and property, and the same approach can be used elsewhere in your application.
You could evaluate properties on visual controls, items in another FNC component, or your own Delphi objects.
Filtering Without Removing Anything
This example also shows why the term filtering becomes broader with the Filter Rules Manager.
Traditionally, filtering means that something either remains visible or disappears from the result.
Here, every appointment remains in the Planner.
The filter is simply being used to determine which rule should be applied to each item.
That result could change appearance, visibility, enabled state or another property. Or, as we've done here, it can be passed to custom application logic through a callback.
And because the condition can work directly with object properties such as Title, this isn't limited to traditional data filtering.
That's where TTMSFNCFilterRulesManager starts to become particularly useful: the filtering engine becomes a way to describe conditions throughout your application, while rules determine what should happen when those conditions are met.
```Gjalt Vanhouwaert
Related Blog Posts
-
Filtering in Delphi: From Strings to Structured Logic
-
Filtering in Delphi: Generating, Parsing and Matching Filters
-
Filtering in Delphi: Visual Filter Building
-
Filtering in Delphi: See the Filter Dialog in Action
-
Filtering in Delphi: Let Users Filter Naturally
-
Filtering in Delphi: Building Modern Filter Interfaces
-
Filtering in Delphi: From Filtering Data to Filtering Properties
-
Filtering in Delphi: Building a Highlighting Property-Based Search
This blog post has not received any comments yet.
All Blog Posts | Next Post | Previous Post