FMX tip – default style lookup names

This one was driving me up the wall – I’m working on (or rather trying to work on) a FireMonkey application, one form in which loads a tree view dynamically. Since each node concerns a certain sort of data object, I wrote a small custom descendant of TTreeViewItem to add a property of the relevant type and a couple of helper methods. So far so good. After that I did a bit of refactoring, separating out the descendant into a custom base class and two child classes, and adding bits and bobs. Following this I reran the program… and found the tree view text had disappeared. Argh!!! Progressively commenting out the new code got me nowhere, until tracing back my step of splitting out the custom descendant into three got me the answer – the now-grandchildren of TTreeViewItem were style-less!

If you check out the FMX source, you’ll see that the default style lookup for an object is its class name, minus the leading ‘T’ and with a ‘style’ prefix. This can be customised if you wish – in the form designer, it’s the StyleLookup property that gets set when you choose ‘Custom Style’ from the designer’s popup menu. If you customise it with the name of a style element that is not part of the active style (e.g., you used the ‘Custom Style’ command in the designer, only to delete the generated style book afterwards), then the default lookup based on the class name is used; and if that doesn’t name a valid style element, then the default style lookup for the parent class is used. If that still results in failure, then the control is just left style-less, and therefore empty at runtime unless the appearance is hardcoded (which is bad form for a FMX control). In my view, if you’re going to start walking up the class hierarchy for a valid style lookup, then you might as well continue to walk until you find one. In fact, more strongly, it surely breaks basic OOP principles for the act of merely splitting out a base class from a class to have the effect of altering (and in this case, breaking) its behaviour.

Regardless, my next step was to assign the StyleLookup property to ‘treeviewitemstyle’ in my custom base class’ constructor. This fixed the original problem of text not appearing… but caused a new issue of check boxes showing even though the tree view’s ShowCheckboxes property remained False! Tracing into the code, I found assigning the StyleLookup property causes a control to actually load its style right there and then; in my custom class’ case, this meant the style was being loaded before the control’s parentage had been established, which was undoubtedly too early (while the default value of the ShowCheckboxes property is False, the default visibility of the check box element of a tree view item style is True). Nonetheless, the fix was easy: assign not the StyleLookup property, but the FStyleLookup protected field:

type
  TMyNodeBase = class(TTreeViewItem)
  public
    constructor Create(const AOwner: TComponent; const AData: TDataObject); reintroduce;
  end;

  TMyRedNode = class(TMyNodeBase)
  //...
  end;

  TMyBlueNode = class(TMyNodeBase)
  //...
  end;

constructor TMyNodeBase.Create(const AOwner: TComponent; const AData: TDataObject);
begin
  inherited Create(AOwner);
  FStyleLookup := 'treeviewitemstyle'; //prevent descendants being style-less
  FData := AData;
end;
Advertisements

6 thoughts on “FMX tip – default style lookup names

  1. Sorry, what ? A field is _directly_ exposed as a protected member ? What sort of madness is this ?

    I can understand the need to have field setters for protected use that bypass the public property accessors for those fields, but the solution in those circumstances – imho – is to provide the required setter, not to just expose the field directly.

    private
    fField: TFoo;
    function get_Field: TFoo;
    procedure set_Field(const aValue: TFoo);
    protected
    procedure SetField(const aValue: TFoo);
    public
    property Field: TFoo read get_Field write set_Field;

    Yes, it relies on a convention for clarity (get_ / set_ for property accessors Get/Set for direct access) but it also protects a class should changes become necessary that require modifications in behaviour when direct access is used.

    It also protects the descendant classes from potential changes in implementation details in the base class.

    I guess the authors of this part of the FMX framework didn’t go to (or fell asleep during) the “Encapsulation” section of their OO training.

    They did get some training, right ?

    Sheesh.

    • “A field is _directly_ exposed as a protected member ? What sort of madness is this?”

      Well in XE2 there were loads more protected fields… On balance, the bigger issue (IMO) is that the property setter forces the style to be reloaded immediately. If it didn’t, and instead just set a flag to say ‘reload the style when it is next called upon’, there would be no need for the protected field in the first place.

      That said, what was done regarding the style lookup is FMX all over – originally even the parent wasn’t looked up, but now it is *and* there is a virtual GetDefaultStyleLookup method, so in principle the ‘classnamestyle’ default lookup name isn’t hard coded any more. However, as I said in the post, surely you shouldn’t just stop at the parent class if a style for the control still can’t be found. Moreover, the GetDefaultStyleLookup method is *only* called upon for the immediate class, not the parent as well – if the lookup code comes to the parent, then the ‘classnamestyle’ convention is hardcoded again. Oh, and if no style is found for a control, there’s no exception raised, despite a style-less control normally being completely blank and useless.

  2. This is very interesting, but I have a problem with XE3 TTreeView controls: The last item in the list cannot be highlighted. OH, for a new FMX annoyance of the week…

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s