I.T. Discussion Community!
-Collapse +Expand
Delphi
Search Delphi Group:

Advanced
-Collapse +Expand Delphi To/From
To/FromCODEGuides
-Collapse +Expand Delphi Store
PRESTWOODSTORE

Prestwood eMagazine

February Edition
Subscribe now! It's Free!
Enter your email:

   ► KBProgrammingDelphi for W...Coding TasksDelphi Downloads  Print This     
  From the January 2016 Issue of Prestwood eMag
 
Delphi Coding Tasks:
Delphi Best Practices 2: Reusable Main Forms
 
Posted 11 years ago on 3/2/2009
Take Away:

How often have you started a new Delphi project, then spent some time fleshing out the default main form provided by Delphi?

This task only needs to be done once or twice. Delphi then allows us to re-use this work at the start of each new project.

Source code is included.

 A file from our File Library ► Delphi Downloads

KB101943

Download Link:

 Download File Now

Introduction 

One of the driving ideas behind Delphi and other OOP development tools is that we don't have to keep reinventing the wheel. By creating Delphi components, and by adding units to Delphi's Repository, we can re-use existing designs and code.

This article focuses on one incredibly common task that should immediately take advantage of this: The creation of an application's main form.  In this article we'll look at what constitutes a "good" main form, build it, and then add it to Delphi's repository. Actually, we'll create two of them.

The Delphi Default Main Form

When you start a new Delphi application, Delphi gives you a default main form - but it needs a little attention before it's ready for "prime time." Depending on the version of Delphi that you're using, you'll probably want to do certain things to turn that default form into a really nice one.

We'll use Delphi 7 for this example, but most of what we'll do is applicable to all recent versions of Delphi.

Main Form 1

Start a new Delphi VCL project and give the main form a name that'll make it easy to re-use.  I've named mine BasicMainForm.

Properties:

Right off the bat, we'll want to change some of the default property values, so let's get started.

Position: It used to be okay to set the form's initial position to poDesktopCenter. This is no longer a good idea because more and more users are splitting their desktops across multiple monitors. You don't want your app to start up split across two screens!

It's probably also not a good idea to use the default, poDesigned, because your screen size can differ significantly from another user's.

I favor setting Position to poScreenCenter. The user can always move it, and, if you take the trouble to save form state, it can be made to appear in the same place the next time your application is run.

ShowHint: Unless you want to suppress help hints, this should definitely be set to True.  We're going to do more about this later, as well. 

AutoScroll: False! You probably do not want scroll bars to automagically appear on your main form; I certainly don't.

The remaining properties are okay left at their default values.

Events:

Whether I'll end up using them or not, I like to double-click on the form's OnCreate event and just put a comment in the handler:

procedure TBasicMainForm.FormCreate(Sender: TObject);
begin
  //
end;

I do the same for OnDestroy.

I do this early because I like these handlers to be right at the top of my forms' Implementation section.  In a moment, we'll be doing something important in the OnCreate handler.

Necessities:

Now let's think about other things that virtually every main form needs...

 A main menu: I don't care how cute Microsoft and others get about replacing the traditional menu with eye-candy substitutes. I think a traditional menu is essential.

Let's drop a TMainMenu on the form. At a minimum, the menu should have top level entries for File and Help (be sure to prefix their captions with an ampersand so the first letter will be a keyboard shortcut).   We'll defer creating other menu items for a moment.

A TActionList (or one of its relatives) is invaluable.  I rarely create any non-trivial form or dialog without an ActionList because TActionList is just too valuable a tool for simplifying our user interface code and managing form "state."  So let's drop a TActionList on the form.

Once the ActionList is on the form, double-click its OnUpdate handler.  Just put a comment in there for now.  As with OnCreate and OnDestroy, I like the ActionList's OnUpdate handler near the top of my code.  I find that I do a lot of poking around in there as the application takes shape.

Now open the action editor and add the one action item that every application needs, an "exit" action.

Name this action "ExitAction."  Set its caption to "E&xit." Set its Hint to "Exit (close) this program."  You can use other hint text if you prefer, of course.

Now double-click on this new Action's OnExecute event, and add a single line of code:

procedure TBasicMainForm.ExitActionExecute(Sender: TObject);
begin
  Close;
end;

When invoked, this action will close your application.  Make this happen by opening our TMainMenu's Menu Designer, clicking in the empty menu item below "File" and setting that menu item's Action to ExitAction. Note that the menu item instantly gets the caption we assigned to our ExitAction item. Nice.  If you look at the object inspector's Events tab, you'll notice that the OnClick event has also automatically been assigned to ExitActionExecute.

If you later decide to change the caption or hint for a menu item, do it to its Action. This becomes very useful when a program option is available in multiple places, like on ToolBars and PopupMenus. They'll all "inherit" their captions, hints, ImageIndex, Enabled, and Visible properties from the ActionItem.

At this point, you can run our little application and test the Exit menu item.

Manifest: Another thing to consider is an application "manifest." Beginning with Windows XP, Windows recognizes application manifests and a manifest is how you get your Delphi application to take on the "look" of an XP program.

Borland thoughtfully added a new component to Delphi, TXPManifest.  Dropping one of these on your main form is all it takes to achieve the XP look.  With the release of Windows Vista, however, the application manifest has grown in scope, so you might consider replacing TXPManifest with a more complete manifest and linking it to your programs.  See our articles about manifests, linked at the bottom of this article.

Status Bar: TStatusBar gives us the ability to provide the end user lots of feedback about what's going on in our applications.  Its SimplePanel property determines whether it behaves like a single panel (True), or if it can contain multiple panels (False). 

Drop a TStatusBar on the form, and set SimplePanel to True.  At this point, our form looks like this:

Basic main form

Now the fun begins; we get to add a bit of useful code.

In the private section of your form class, define this method:

procedure DisplayHint(Sender: TObject);


In the Implementation section, add this code:

procedure TBasicMainFormTemplate.DisplayHint(Sender: TObject);
begin
  StatusBar.SimpleText := GetLongHint(Application.Hint);
end;


Finally, in our form's OnCreate handler, add this line of code:

Application.OnHint := DisplayHint;


If you run the application again, and hover over the File | Exit menu item, you'll see the hint we assigned to ExitAction displayed in the status bar.

That's nice, but we can do better.  It may be that a future application will need to use a multi-panel status bar, so let's code in anticipation of that case.  We'll change the implementation of DisplayHint to this:

procedure TBasicMainFormTemplate.DisplayHint(Sender: TObject);
var
  hintText : string;
begin
  hintText := GetLongHint(Application.Hint);
  if StatusBar.SimplePanel then
    StatusBar.SimpleText := hintText
  else if StatusBar.Panels.Count > 0 then
    StatusBar.Panels[0].Text := hintText;
end;


Now, if you ever need a multi-panel status bar, simply change its SimplePanel property to false, use the StatusBar's Panels Editor to define your panels, and your help hints will still work.

Now we have a pretty nice, re-usable main form. We can add it to our Delphi repository now, and never have to repeat any of this work again.

To do that, simply right-click on your form, choose Add To Repository, and fill in the dialog:

Add to Repository Dialog

Note the "Page" drop-down. The selection you make here will determine the tab upon which your form will appear in the "New Items" dialog that appears when you choose File | New | Other in Delphi.

Main Form 2

Now we'll take our main form a little farther to support a TToolbar and its necessary TImageLists.  We have some choices, now:

We could defer putting our basic main form in the repository, and wait until we have this more complete main form ready. But there are some simple applications and utility programs that will be served just fine by our basic main form, so I like to have both at hand.

Inherit or Copy?

We could add this basic main form to the repository, and then inherit our new form from it.

We could add this basic main form to the repository, and then ask for a copy of our basic main form.

I'm choosing to do the latter because by not inheriting, we remain free to remove features from our basic main form. (If we inherit from the basic main form, we're forever stuck with all the components on it.)

So let's use File | New | Other, choose the Forms tab, select our Basic Main Form, be sure the radio button "Copy" is checked, then click OK.

Name this new form MainFormWithToolbar, and save.

Now drop a TToolbar on the form.  We won't be adding any buttons because we don't known what buttons might be needed in new applications.

Also drop six TImageLists onto the form. Note that their Height and Width properties default to 16 pixels.  Multi-select three of them and change Height and Width to 24.  This is the size commonly used on toolbar buttons.

Our form now looks like this:

Main Form 2

I like to name these ImageLists as follows:

  • DefaultImages24
  • HotImages24
  • DisabledImages24
  • DefaultImages16
  • HotImages16
  • DisabledImages16

Why all the image lists?  Toolbar buttons are designed to display one of three images, depending on the button's state.  The "default" image is what's normally displayed.  As the user hovers over a toolbar button, the "hot" image is displayed.  It's slightly more saturated than the default image. If a toolbar button happens to be disabled, the "disabled" image is displayed.

The 16x16 image lists are useful for display on menus, which offer a little less space than toolbars.

To make this all work, we need to set a few more properties.

Click on the TToolbar, and set the following properties:

DisabledImages: DisabledImages24

HotImages: HotImages24

Images: DefaultImages24

Click on TMainMenu, set the sole images property, "Images" to DefaultImages16.

Now for one final bit of "glue."  Click on the TActionList and set its Images property to DefaultImages24.

At this point, we can add this more complete form to the Delphi repository.

The next time we start a new project, we can throw away the default main form that Delphi provides, then select the most appropriate of our two main forms.

Even Slicker

But Delphi offers a nifty convenience.  If we use Tools | Repository, we can use this dialog to make one of our main forms the default form Delphi will use for new projects.

Repository Settings Dialog

Once we use this form for a new project, we want to observe a few good practices so everything works nicely:

No explicit OnClick event handlers! We want our TActionList to take care of all this, for a couple of good reasons.

Instead of OnClick handlers for menu items and toolbar buttons, add ActionItems to the TActionList.  For each item, you'll want to do these things:

1: Make sure you have corresponding images in your TImageLists.  You need to take pains that the images in each of the images in our lists have the same index value.  In other words, they all need to be in the same order.

2: For each action item, we want to specify the ImageIndex for the image we want associated with the action.  We also want to provide an appropriate caption and hint.  And, of course, we want to write appropriate handlers for each item's OnExecute event.

That done, we assign the appropriate action to each toolbar button and menu item.

Real Magic

Amazing things now begin to happen.  Toolbar and menu images are pulled automatically from our ImageLists as needed.  Captoins and hints also come from our TActionList, and hints display as both pop-ups, and in the status bar.

But here's where it gets even better:

The Real Power of TActionList

TActionList has an OnUpdate event.  It fires every time the user does something and the program goes back into its message processing loop. This handler is the ideal place to centralize all the code that manages the state of your user interface. 

In your ActionListUpdate handler, you can test for various conditions and set various action items' Enabled and Visible properties as appropriate.  Your user interface immediately updates its button and menu items to reflect your directives in ActionListUpdate.

Note: TActionLIst isn't just for main forms.  Most non-trivial dialogs can benefit from a TActionList.  Think about creating a "wizard" using a TPageControl, multiple tabbed pages (tabs hidden of course), and managing the Next, Back, and Finished buttons. Managing the enablement and/or visibility of those buttons is nicely handled in an ActionList's OnUpdate handler.

Conclusion

You now have two ready-to-use main forms; forms that are pre-wired to provide full UI support to just about any application you can imagine.

And you never again need to repeat the steps above. True, none of them were very difficult, but why do the same old thing over and over?

Download Link:

 Download File Now

More Info

KB Post:  Application Manifests, Revisited for Vista
Article:  Delphi Best Practices I - Handling TForm.OnCloseQuery
Article:  Get the XP Look in Older Versions of Delphi

Comments

0 Comments.
Share a thought or comment...
 
Write a Comment...
...
Sign in...

If you are a member, Sign In. Or, you can Create a Free account now.


Anonymous Post (text-only, no HTML):

Enter your name and security key.

Your Name:
Security key = P1227A1
Enter key:
Article Contributed By Wes Peterson:

Wes Peterson is a Senior Programmer Analyst with Prestwood IT Solutions where he develops custom Windows software and custom websites using .Net and Delphi. When Wes is not coding for clients, he participates in this online community. Prior to his 10-year love-affair with Delphi, he worked with several other tools and databases. Currently he specializes in VS.Net using C# and VB.Net. To Wes, the .NET revolution is as exciting as the birth of Delphi.

Visit Profile

 KB Article #101943 Counter
26002
Since 3/2/2009

Follow PrestwoodBoards on: 


©1995-2020 PrestwoodBoards  [Security & Privacy]
Professional IT Services: Coding | Websites | Computer Tech