views:

926

answers:

2

Hi, I have been learning Delphi for the last 3 years, on a hobby/occupational level. I am happy to say that I have now progressed to the point that I can look back on my early code with horror and embarrassment. So I am now going through some of my early apps and rewriting/ refactoring them.

One of the bad habits I am trying to get away from is accessing components on one form from another unit. In an effort to enforce this, I have been experimenting with using frames as a method of information hiding. So instead of having a form with components on it, I am creating a frame to hold all the form components, then placing the frame on a form, moving the frame declaration into the private declarations,

type
  TMyForm = class(TForm)
   private
    MyFrame: TMyFrame;
    procedure SetTimeDate(const Value: TMyItem);
    function ReadTimeDate:TMyItem ;

then registering the frame in the form initialization section

initialization 
begin
RegisterClasses([TMyFrame])

I am then declaring the properties I need in the public section of the form unit, which has access to the frame and its components.

  public
    property TimeDate: TOverlayItem  read ReadTimeDate  write SetTimeDate;

I am also using frames to consolidate often repeated component groups.

This seems to work for the purposes I want (hiding Myframe and its components), but does anyone else have any experience of this method?

Are there any drawbacks with using frames? Am I actually gaining any benefit from doing this? Are there any problems with using nested frames within frames? Are there any good practice guides to using frames in Delphi? Are there better/ easier ways of achieving the same effect with regard to GUI information hiding in Delphi?

HMcG

+2  A: 

It sounds like you are still having a lot of logic in your UI layer. Forms/Panels shouldn't have that much Value properties (except maybe for Dialogs).

If you want more structure than read up on the MVC pattern.

Having said all that, Frames can be a good way to organize the GUI. Like with everything, don't overuse.

Henk Holterman
You are quite correct in that my early apps had all the logic in the UI layer. So what I would like is a clean way to raise UI values/properties for use in the main app unit. The majority of my apps are interfacing to hardware and are doing fairly simple operations, but with a lot of specific settings, so it is not clear how to get away from the form (dialogs?) having a lot of Value properties.I am working (slowly) through the Head First Design Patterns book so I should get to MVC soon. What I had hoped for was a cleaner RAD style for simple tools/apps.
HMcG
RAD won't lead you to a better design. Consider creating a Settings object that the Form can operate on.
Henk Holterman
So I create a record or class that the logic in the GUI layer sets the values on, then access that object in the main unit? This does sound like a better method than having a bunch of properties in the GUI form unit. Thanks.
HMcG
Yup - it's called a Business Rules Layer
Henk Holterman
A: 

I like frame generally for creating complex reusable bits. For the most part I think they can be a really clean way to construct screens. However, as mentioned by Henk Holterman your screens and frames should only contain logic relating to the functioning of the UI and be as ignorant as possible about the business logic.

A couple of points re frames and information hiding in the UI:

  1. As discussed in another question on StackOverflow you need to be careful when using event handlers in your frame.
  2. Frames still have lots of published properties and do not really resolve the issue of forms being able to inappropriately fiddle with one another's bits. Even if you don't do it, if the code permits it someone will eventually write code that tampers where it shouldn't. I always remove the global form variable Delphi sullies the code with and often write wrapper objects or implement interfaces that provide controlled access to the UI.

So instead of having code like this:

ClientForm := TClientViewForm.Create(Self);
try
  ClientForm.Client := MyClient;
  ClientForm.ShowModal;
finally
  ClientForm.Free;
end;

I'll generally force people to write something of the sort:

ClientViewer := TClientViewer.Create(MyClient);
try
  ClientViewer.Show;
finally
  ClientViewer.Free;
end;

or even

TClientViewer.ShowClient(MyClient);

and have the class method ShowClient handle the bit shown in the first listing. That way the calling code never receives the form pointer and cannot touch any bits that are not provided specifically by the wrapper class.

Cobus Kruger
Thanks for pointing out 1) - it is not something I had come across yet.Point 2) is what I doing with the MyFrame being private - only the MyForm unit associated with Frame can access the Frame and the frame components. None of the other units can see the (private) MyFrame.If the wrapper class method has the same effect, this would probably be a simpler way of achieving the same effect. HMcG
HMcG