Posts from May 2012
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:
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.
 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.
Build 4 beta is now available. Chief changes are improvements to property editors and property editor styling.
Full change list:
Fixed: GradientPointsEditor text values update when gradient points are selected (Update 4 bug? ColorPanel.OnChange is no longer called when value is set programmatically).
Fixed: Added editor for TControl.Position.*
Fixed: R and B values where swapped in text fields of GradientPointsEditor.
Added: Text entry for ColorEditor.
Fixed: GradientPointsEditor gradient updates when changing text fields.
Fixed: Files other than current one not being saved on SaveAll or Close.
Fixed: Alpha value (text edit) updates properly when selecting gradient points (FMX bug workaround).
Added: Property editor styling is slightly better.