Using frame inheritance, possibly in combination with the frame factory pattern (although that is out of scope for this post) can considerably help you limit dependencies, reduce the number of forms and separate concerns of your application.
What’s the difference between forms and frames?
Many Delphi programmers use nested forms to structure their applications, and while this is a perfectly good way to compartmentalise a frame, it has drawbacks.
As it turns out, one of the more costly operations in Windows when it comes to resources and execution time is – oddly – creating windows. Indeed, this has improved over the years but creating a lot of forms in your applications still costs time and resources. If you nested three or four forms on your main form, it would require the time and resources of creating four or five windows.
Frames, on the other hand, does not have a window handle and doesn’t go through the process of allocating a window. Thus it achieves the same functionality as nested forms, but at a lower cost.
Another consideration is that FMX doesn’t appear to support nested forms very well, at least not in the simple tests I did. I did manage to change parenthood of a form to sit on another one, but I didn’t manage to get rid of caption bars etc. Perhaps I didn’t try hard enough, but this change in behaviour compared to VCL should at least be a consideration in what pattern you choose.
So why use frames?
You can look at a frame as a separate and stand-alone TPanel of sorts, one that has its own unit and code base. And how often haven’t we used panels to compartmentalise a form in separate areas? Of course, this might be the most appropriate thing to do in many cases, for instance when different component groups need to be visually distinct or grouped but belong to the same data or functionality.
But quite often, there is the need for showing information related to other information on the form. A master/detail relationship might be a good example. In the early days, I would add all the code to maintain both the master and its details in the same form unit, which would almost always lead to huge code bases in that unit and redundant code spread over the application as I would almost always need the same details shown in other parts of the application.
Placing the details on a frame, and giving the frame the capability of displaying and maintaining details for a given master (through a read/write property for instance) allows me to reuse the same code for details in different parts of my applications, or even across different applications.
Where does frame inheritance come in to play?
Imagine that you have several master/detail relationships in your business data. That’s not all too uncommon. To accommodate several such master/detail relationships, you’d have to create several forms handling all those master/detail structures, adding all the edit components, grids to show the details, add-, edit- and delete buttons etc. It can get quite tedious, and it will almost certainly create redundancy of code.
Instead, you set up one generic TForm or TDialog, and add a class frame (TMasterFrameClass) for the master data and a class frame (TDetailFrameClass) for the detail data. These frame classes are specialised for handling saving, deleting and getting the master data you want, or handling all the operations needed to get the detail grid to work to your liking.
The TMasterFrameClass and TDetailFrameClas, however, doesn’t know anything about the actual data you need to show or manage. Instead, this level is specialised to handle only common things like triggering specific virtual methods when, for instance, the user clicks on OK or Cancel.
The actual data awareness is introduced in the next layer, in the – if you will – TCompanyFrameClass (of TMasterFrameClass) and the TEmployeesFrameClass (of the TDetailFrameClass). This is the layer that knows about your specific data.
Your application is a layer cake. By using frame inheritance (and form inheritance for that matter), you can structure the UIs of your application into robust, separated and isolated view classes. Just like ordinary class inheritance, you can use frame inheritance to create compartmentalised forms and dialogs with a minimal and irredundant code base.
In one project a few years ago, I decided to make an extra effort to wrap my ideas of frame (and form) inheritance into a generic framework. The idea was to set up frame- and form classes that I could easily reuse in other Delphi projects.
While that idea had some bearing, it fell flat for a few reasons.
- At the time, I didn’t have the experience and know-how to create a good structure of classes for what I needed to do
- There were (and still are) some bugs where the Delphi IDE has difficulties finding ancestor frame classes stored at other locations than the Delphi project. It is possible this is not an issue using Git submodules.
- Lack of time and resources
However, I do believe there would be benefits from creating such a framework. In my experience, much time that is spent when developing applications with Delphi is spent setting up similar form and frame structures to what I’ve done many times before. This is especially true when using FMX to build cross-platform applications.
It’s possible there are such frameworks, but I haven’t seen any. Perhaps TFrameStand by Andrea Magni (github) can be used for such a purpose, but I haven’t looked at it to have an informed opinion.
- Separate concerns in your code, isolating functionality to a specific data model
- Reduce code redundancy
- Achieve functionality injection through combining frame inheritance with frame factories and interfaces
- Modularity in creating application forms and dialogs
- Reduce the number of forms used by the application