t e m p o r a l 
 d o o r w a y 

Visual Component Alignment and Complex Forms

 

A frequently difficult issue is creating forms that adapt to the user's needs for screen real-estate. It is critical to be able to do this, for several reasons...

  • No screen is large enough for the user with many tasks.

  • A cooperative application may need to be resized so that another application is available to offer objects for drag and drop, or so its draggables can be dropped on another application.

  • In multiple window interfaces, the user should be free to resize the user interface of each window into a convenient configuration.

It is simple enough to create a form that is resizable, but when the form is shrunken, controls disappear behind the edges, and when the form is made larger, the controls are surrounded by bare space.

The base form has a set of components in a particular arrangement.You can see the splitters (in purple) that allow for a resizing the components manually.
But when the form is resized, the components don't follow the form. Here we see the extra margin left when the form is dragged larger...
And here, the form grows scrollbars, but now some of the controls are missing - at least until the user scrolls the form.
But when alignment is used, the form components resize adaptively. When the form shrinks the "client" aligned components get smaller...
And when enlarged, they grow.

The "aligned" form has the following components (you can paste this onto your own form in the IDE)

object Panel1: TPanel
    Left = 186
    Top = 0
    Width = 185
    Height = 300
    Align = alRight
    Caption = 'Panel1'
    TabOrder = 0
    object Splitter1: TSplitter
       Left = 1
       Top = 151
       Width = 183
       Height = 5
       Cursor = crVSplit
       Align = alTop
       Color = clPurple
       ParentColor = False
    end
    object ListView1: TListView
       Left = 1
       Top = 1
       Width = 183
       Height = 150
       Align = alTop
       Columns = <>
       TabOrder = 0
    end
    object Panel2: TPanel
       Left = 1
       Top = 156
       Width = 183
       Height = 143
       Align = alClient
       Caption = 'Panel2'
       TabOrder = 1
       object Splitter2: TSplitter
          Left = 106
          Top = 1
          Width = 5
          Height = 141
          Cursor = crHSplit
          Color = clPurple
          ParentColor = False
       end
       object RadioGroup1: TRadioGroup
          Left = 1
          Top = 1
          Width = 105
          Height = 141
          Align = alLeft
          Caption = 'RadioGroup1'
          TabOrder = 0
       end
       object Memo1: TMemo
          Left = 111
          Top = 1
          Width = 71
          Height = 141
          Align = alClient
          Lines.Strings = (
             'Memo1')
          TabOrder = 1
       end
    end
end
object TreeView1: TTreeView
    Left = 0
    Top = 0
    Width = 186
    Height = 300
    Align = alClient
    Indent = 19
    TabOrder = 1
end

Here you can see the

The form starts completely empty.

Next, add the panel on the right (Align=alRight) and size appropriately. Remember, this will not change width to follow the form - only height.

Then add the treeview to the left and align to alClient. The treeview will now change size as the form is adjusted. This is a common strategy when a control panel contains buttons or other controls that shouldn't be or won't take well to resizing.

It is usually a good idea to put a splitter between these two components, so that the user can override the developer's fixed size for the control panel as needed.

Next on the Panel1, add the ListView as Align=alTop, the splitter as alTop, and the Panel2 as alClient.

Again, in the change from the prior step to this one, you can see a frequent pattern in component layout...

  • Break down the layout into major left to right sections, with one section alClient and the other left or right.

  • Within each of the horizontally arranged components, as appropriate, break down their layouts into two sections, with one section alClient and the other top or bottom.

  • Repeat the above process until the layout is complete.

It is possible, though much less common, to also put two horizontal layouts inside each other or two vertical layouts inside each other. It is slightly more common than that to have multiple components with the same alignment (top, left, bottom or right) and one client component. One example of that is the placement of the splitter on this version of the form; both the listview and the splitter are alTop.

The final version puts a horizontal layout into the Panel2.

The extensive use of alignment and splitters provides a highly flexible user interface. However, there are other important techniques as well.

Anchors offer an alternative to alignment. I only recommend their use, however, when alignment can't be used or won't work. The following is a classic example - two buttons which need to stay near the top and bottom of the form as it is assigned.

Here's the form with the two buttons. The panel is alRight, and the treeview is alClient. The top button has Anchor->Top and Anchor->Left = true (the default), and the bottom has Anchor->Left and Anchor->Bottom = true.
As the form resizes in height, the bottom button moves to stay visible.
But this can be pushed too far.

 

For instance, some components don't have an Align property - this included the TButton. Even if they did have it, there is a certain minimum size below which a control panel becomes unable to adapt. A TScrollbox can be used to help with that...

The scrollbox can be used to deal with the problem of controls overlaying each other.
But this is no panacea, since the controls are fixed in position and will vanish when the form is shrunken.
But they can be scrolled into sight if needed. Thus, the bottom controls should always be those less necessary for the operation of the form.

Cautions

  • These techniques do not deal with issues of font sizing. You can use a component like GTSizer++ to manage font sizing, but you will not be able to use alignment in a GTSizer user interface.

  • Resizable user interfaces have limits. Use all of the techniques at hand - including TPageControl, TScrollBox, alignment and anchors - to reduce the complexity and prioritize the elements of the user interface. Trade off appropriately between stable regions and client regions.

Conclusion

Creating a good user interface is equal parts art and science. You need to consider the human factors of the interface and the elegance of the presentation. You need to think about what is critical, what is important and what is optional. You need to consider what aspects of the user interface need to be visible all the time and which can be safely allowed to be hidden or obscured. If possible, work with a trained graphic designer and a usability engineer, or gain that expertise for yourself.

Copyright © 2004 by Mark Cashman (unless otherwise indicated), All Rights Reserved