10 Things Every FireMonkey Developer Should Know About Styles

A recent Embarcadero webinar video by Eugene Kryukov (FireMonkey designer) and Vsevolod Leonov (FireMonkey evangelist) gives a really good, in depth look at FireMonkey styles and how they work. Here are some tips I distilled down from that video along with a few things I’ve learn myself.

1. How a style name is constructed

You’ll want to know this if you’re creating custom controls. If you don’t explicitly state a style name for your control (and you probably don’t want to) FireMonkey will search for a style based on te class name of your control - it removes the preceding ‘T’ and appends ‘Style’ to the end.

So, a TButton uses a style ‘ButtonStyle’. A TEdit, ‘EditStyle’, a TCalender ‘CalendarStyle’ and so on.

2. Style names can also be inherited

But if you’re subclassing a component you may not want to have to create a style for it if you haven’t modified anything which alters the styling. And if that’s the case you don’t need to.

If you create a component:

type TEditChild = class(TEdit); 

FireMonkey will first look for the style ‘EditChildStyle’ (as we saw above). If that doesn’t exist it will look for the style for the parent class, i.e, it will look for ‘EditStyle’ and apply that.

But, this doesn’t apply to grandchildren. If you then create:

type TEditGrandchild = class(TEditChild); 

FireMonkey will look for ‘EditGrandchildStyle’, then ‘EditChildStyle’, but it will stop there and not go any further up the component tree to ‘EditStyle’.

3. Loading default styles

The basic look of your application comes from the default style. If you want to change this you need to set the StyleFileName property of your forms Application object (in the FMX.Forms unit):

Application.StyleFileName := 'C:\Styles\MyStyle.style'

This is best done in your project file, before the line to Application.Initialize.

Ideally there should be some way to set the default style from within your applications project options, or to load it from a resource. Sadly, after much experimentation, I can’t find a way to do this. If you know better, please comment.

4. Where your application gets it’s styling info

As you know you can pop a StyleBook on a form to customise your style. But adding a separate stylebook to each and every form: duplicates data; increases redistributable sizes; is a mainainance nightmare (update every form when you change a style?); just isn’t DRY.

Much better to create a single master StyleBook on your main form, then link each child’s stylebook to that on the main form. You can do that with the following in each forms OnCreate event handler:[1]

StyleBook := TForm(Application.MainForm).StyleBook1

**Update: If you do this make sure you set the StyleBook property back to nil before freeing the form. If not you’ll get AVs if the style is reloaded, and possible in other scenarios due to a hanging pointer.

Note, however that this doesn’t work within the form editor. Create a custom control, add it to your child form and it won’t show the style you have loaded in your main forms StyleBook. I really hope Embarcadero find some kind of workaround for this. Or better yet, improve the style book handling within FireMonkey.

5. Set your StyleBook property

This gotcha has caught me a few times: You add a StyleBook to your form, change the style within it, set your components StyleLookup property and run your application. And you get the default style.

You should have set the forms StyleBook property to point to the StyleBook. Ouch.

Note that if you right click a control and edit it’s style, a StyleBook will be created and the StyleBook property will be set, but only if there isn’t already a StyleBook present. If you create the StyleBook manually, you need to set the StyleBook property yourself.

6. You can have multiple StyleBook objects

Your form can have multiple StyleBook objects. I figure that explains the behaviour in the above item - FireMonkey doesn’t want to assume whcih StyleBook to use. Why would you want multiple StyleBooks? I’m not really sure. The form and the components on it can only use one at a time.

7. Styles don’t have to come from style files

So, you know a style can come from the default style, or from a StyleBook, but it can also come from a control on a from. Simply set the StyleName property of a control and set the StyleLookup of another control to match.

8. Keep your StyleBooks light

The resources in a StyleBook take time up at form create time. For this reason you should keep your StyleBooks to a reasonable size. Instead you should put styling information in your default style (and load it with Application.StyleFileName - see above).

Sadly at the moment there’s no easy way to merge style files - so you’ll have to add your custom styling to the (probably Embarcadero supplied) default style and keep the merge up to date.

9. Beware the HitTest

Add a TImage to a button and you’ll notice that when the mouse is over the image the functionality of the button will be lost. I.e. mouseovers, clicks etc will be ignored. This is because the TImage is absorbing them. To fix this you needs to set the TImage’s HitTest property to False. This applies whether you’re adding controls on a form or in a style file.

10. If all else fails, set Locked := True

Every FireMonkey control has a Locked property. I’m not sure what this does. The Embarcadero doesn’t seem to know what it does either, the property reference simply says it locks controls at design time, but this page says “Enabling Locked changes the way hit testing works and triggers fire, so that the subcomponent is part of the larger whole.”. My experience (although somewhat limited in this area) is that if I’m having problems with mouse clicks going missing fiddling with Locked in association with HitTest (see above) is the best way to resolve things.

[1] An even better way to handle this is to create a custom form class, and set the StyleBook property in an overridden Create constructor. This is left as an exercise for the ready.


Previous Comments

#1 from .(JavaScript must be enabled to view this email address) on June 12, 2012

Hello, oversized for the problem in topic 3, maybe my solution will solve the problem.
Macgayver Armini Apolonio

#2 from .(JavaScript must be enabled to view this email address) on June 12, 2012

Many thanks for that link. I’ve been searching for how to do that for ages.

#3 from .(JavaScript must be enabled to view this email address) on July 11, 2012

You can also use ResEdit and replace the default style string (which takes-up most of the space) I used it to remove all the tabs chars and styles for unused controls and it reduced my exe to half the size

#4 from .(JavaScript must be enabled to view this email address) on October 31, 2012

6. You can have multiple StyleBook objects
[snip] Why would you want multiple StyleBooks? I’m not really sure. The form and the components on it can only use one at a time..

If you add a TLayer3D onto a TViewport3D then the layer needs another Stylebook. These are useful when wanting to add fancy 3D effects to areas of your mainly 2D app.

Thanks for the useful info in the blog btw!

#5 from .(JavaScript must be enabled to view this email address) on February 07, 2013

For Topic 2)
Under XE3, i tried :
type TEditChild = class(TEdit); 
But style was not inherited

I had to force it at creation time :
StyleLookup := ‘EditStyle’;

#6 from .(JavaScript must be enabled to view this email address) on February 16, 2013

Crafty, all should still work fine under XE3. I expect your problem lies elsewhere.

Commenting is not available in this channel entry.