The new FireMonkey components have extensive design time functionality through the style engine so the presentation of components can be easily varied by the style editor. Some of the style parameters are also included in the object properties such as Font and FontFill (the new way to change the font color) but if the object code does not expose the parameter you need how do you change the style information dynamically at runtime? I came across this problem with a TEdit control. When a user puts unacceptable data into an edit box or fails to populate mandatory fields I like to be able to highlight these by changing the background to yellow. When converting this code to FireMonkey I was unable to find a Color property for a TEdit Box.
I have now produced a simple routine which achieves this function and in the process learnt a lot more about the style engine.
My solution consisted of finding the appropriate Style Object and changing the color value there. Fortunately the background rectangle of the TEdit style has a “style name” in the standard implementation which makes recovery of this object easy using the FindStyleResource method of the TEdit object. Do not use the generic FMX.Types.FindStyleResource or you may end up changing the color of the wrong component.
From the Style Text
object TRectangle StyleName = 'background'
Having obtained the correct (TRectangle) object it is possible to change the color property and repaint the component.
procedure SetEditControlColor(AEditControl: TCustomEdit; AColor: TAlphaColor); var T: TFmxObject; begin if AEditControl = nil then Exit; T := AEditControl.FindStyleResource('background'); if (T <> nil) and (T is TRectangle) then if TRectangle(T).Fill <> nil then TRectangle(T).Fill.Color := AColor; AEditControl.Repaint; end;
This only works because the background rectangle had a unique style name. Many objects in the style file do not and if you need to modify those objects you could apply your own style file assigning a style name to access the style object but that is not nearly so attractive. Style names seem to be assigned so that the component code can get access to style objects so maybe the intention was always there to expose either the background rectangle or its color as TEdit properties and they may appear in the future.
The fact that component code relies on information in the style file in this way also raises the possibility that editing a style file could inadvertently block some component functionality.
What you’re doing is very tempting, but suppose you want to change the colour scheme in future, suppose the next version of windows uses a very pale grey for the background colour instead of white. Your controls will now look slightly wrong. Or suppose somebody else uses your code but wants the ‘error’ colour to be red. In either case you (or they) have to pour through the code and find and change the hard coded colour references. Not nice.
Not to mention that your code above assumes the background colour is set as a solid colour. There are a number of ways to specify the background of a FireMonkey Style and if the default changes in a future version your left with a silently failing bug.
I much prefer to maintain a complete separation between the styling and the coding. You can write the code, a style designer can create the style. And if you want to change anything about the visual appearance you only have one place to look: the style.
In the scenario you have above,I would create two TBrushObjects as part of the style (or custom style). One will be the normal brush, one the error brush. At start up do what you do above, but set the BrushKind property to bkResource and point the Fill.Resource property to the normal brush. On invalid input change the Fill.Resource to the error brush.
Another way would be to create a custom control descended from TEdit with an IsInvalid property. Then add a TInnerGlowEffect below the background object of the custom style with a trigger of IsInvalid=True.
Then the magic will happen automatically and as an added bonus anyone reusing your component could replace your TInnerGlowEffect with a different effect, or an animation when the controls contents become invalid.
Mike – I think Roger’s article is more about the ‘how’ of working with the elements of a FireMonkey component and lifting the lid on styles for mainly new users. I don’t think Roger is promoting this as a catch-all solution for colour-management in large projects that are to be maintained.
I am a total heathen on the concept of a style designer controlling the visual effect. Not that I disagree with it, just that until XE2 I have never felt the need. I can understand that on a large project a managed style environment could be very useful in maintaining a consistent look and feel. In an unmanaged environment FireMonkey styles have the potential to make implementation very convoluted.
The potential for a style manipulation to cause “a silently failing bug” was noted in the original post but it seems to me to be more than an issue with this simple procedure. Code interaction with style components appears to be embedded in FireMonkey component code and style designers will need to be aware of that.
I can see a definite need to create ‘design contracts’ when creating and using FireMonkey styles. For example, when creating a custom style there needs to be communication as to what the various style elements will be called.
And when creating a custom control + style the designer and developer will have to agree which styling components need to be accessible to the code, what the classes of those components will be and their ‘stylename’s. For example, that a button will have a TText named ‘Text’ and a TImage named ‘Image’.
(BTW, I agree with you on never having needed or thought about is before XE2).
Hello. Someone can tell me if it is possible to change, in TCustomEdit control, color or style of _single_ word (i.e. to set style and color properties of differents words indipendently)? Thanks.
@NickBlu – I think for that you’ll need some kind of rich edit control which doesn’t currently exist, at least in the built in components.