Data binding¶
A binding connects a value in your application to elements of the drawing. You say which elements with a selector, and what to write, an attribute or the text content, and then assign the value. The component writes it and repaints.
Bindings replace the find-and-set code of the previous pages for the common case: a status drives a colour, a measurement drives a label or a needle.
A first binding¶
Every SVG component has a SVGBindings collection, so bindings can be set up
in the Object Inspector as well as in code:
uses
BVE.SVG2Bindings,
BVE.SVG2Types;
procedure TForm1.FormCreate(Sender: TObject);
begin
SVG2Image1.SVGBindings.Bind('needle', 'transform');
end;
procedure TForm1.SetPressure(const aBar: Double);
begin
SVG2Image1.SVGBindings[0].Value :=
Format('rotate(%.1f 50 50)', [aBar * 2.7], USFormatSettings);
end;
Assigning Value writes it to every element the selector finds and repaints
the component. There is no separate apply or repaint step.
Choosing the elements¶
SelectorKind decides how Selector is read:
| SelectorKind | Selector is | Finds |
|---|---|---|
skID |
an element id, 'needle' or '#needle' |
that element |
skTag |
an element name, 'rect' |
every rect in the drawing |
skClass |
a class name, 'valve' or '.valve' |
every element with that class |
skAttribute |
an attribute name, 'data-tag' |
every element carrying it |
An id may be written with a leading # and a class with a leading ., as in
CSS. The prefix is optional.
Only skID is limited to one element. The other three drive a whole set from
one binding:
// Every element the drawing marks class="alarm" turns red at once
SVG2Image1.SVGBindings.Bind('alarm', 'fill', skClass).Value := 'red';
For skAttribute, SelectorValue is the value to match. Set
MatchSelectorValue to False to match every element that has the attribute,
whatever its value:
var
b: TSVGBinding;
begin
b := SVG2Image1.SVGBindings.Bind('data-tag', 'fill', skAttribute);
b.SelectorValue := 'PT-102';
b.Value := 'orange';
Choosing what to write¶
TargetKind is either tkAttribute, which writes the attribute named by
TargetName, or tkText, which replaces the text of the element. Text is what
a dashboard needs to show a reading:
Value is a Variant. For text it is converted with VarToStr; for an
attribute a number is written in SVG syntax, with a dot, so it needs no
formatting. Values you build as strings, such as a transform, still need
USFormatSettings (see
Numbers in SVG always use a dot).
Two bindings on one element¶
Two bindings may write the same attribute of the same element. A group of lamps can be lit all at once by class and individually by id. The element keeps whatever was written last.
Assigning a binding a value it already holds does nothing, which keeps a
binding cheap to drive from a timer. But "already holds" is about the binding,
not the element: if another binding has written the element since, a plain
assignment will not write it again. Apply always writes:
// Light every lamp at once, by class
FAllLamps.Value := ColorAlarm;
// ...and later put each lamp back to its own state. The value may be
// unchanged since before the lamp test, so assigning it would do nothing;
// Apply writes it anyway, over what the class binding left behind.
FLampReactor.Value := LampColor(ReactorHigh);
FLampReactor.Apply;
Loading another drawing¶
Bindings are not tied to the elements of one document. After the component loads a drawing, it finds the elements again and writes the current values to them. Bindings set up once in the designer keep working when the drawing is replaced, as long as the new drawing has the elements the selectors name.
A selector that matches nothing is not an error; the binding simply writes
nowhere. ElementCount tells how many elements a binding currently drives,
which is the quickest way to check a drawing against the application:
if SVG2Image1.SVGBindings[0].ElementCount = 0 then
ShowMessage('The drawing has no element for this binding');
Cost¶
A binding finds its elements once and keeps them, so driving it from a timer
does not search the drawing on every tick. Only skID is answered from the id
index; the other kinds walk the drawing the first time they are used, and again
after a drawing is loaded or the selector changes.
A worked example¶
Examples/DataBinding in the
examples repository
drives a schematic of a small chemical process entirely through bindings: feed
tank, pump, heater, reactor, valve, product tank and three gauges. It uses
every selector kind and both target kinds. The drawing that comes with it shows
how to prepare an SVG for binding: ids on the parts that move, a class on the
pipes and the alarm lamps, and a data-tag on each instrument.
Frameworks¶
Bindings work on VCL, FMX and FPC/Lazarus: TSVG2Image, TSVG2Control and
TSVG2WinControl on VCL, TSVG2Image and TSVG2Control on FMX. The engine
itself, BVE.SVG2Bindings, does not depend on a framework and works on an
ISVGRoot, so it can also be used without a component. The FPC components,
TSVG2Image, TSVG2Control and TSVG2WinControl, have the SVGBindings
collection as well.
The members of the collection and of a binding are listed in the API reference.