A fancy expandable dialog box

by Mark G. Wiseman

Like some college courses, this article has a prerequisite. Before reading it and using the information contained it, you really need to read the article A simple expandable dialog box. This article is also in this issue.

What’s so fancy?

Well, the fancy expandable dialog uses more resources than a simple expandable dialog and it’s slower. But it’s still simple to create and it looks really cool!

The simple expandable dialog just pops into and out of its expanded state. The fancy expandable dialog uses animation to expand and contract.

When its Expanded property is set to true, the dialog expands by first rolling down the bottom of the dialog and then two doors open to reveal the controls of the expanded section.

What’s the design?

Let’s start off in the IDE by creating a dialog box exactly the same as the simple expandable dialog. Make a note of the bottom panel’s Height value. In my version, the Height is 253.

Once that’s done, drop two more panels on the bottom panel of the dialog. These will be our doors. Set the Align property of one of the panels to alLeft and the set the other to alRight. Now adjust the Width property of both panels until the interior edges of the panels just touch—like two closed doors. To make it easier to code, make sure that Width has the same value for both panels. In my design the Width is 245.

Now make a note of the panels’ Width value. Then set the Width property of both panels to zero. The panels will disappear (the doors are open).

That takes care of the modifications to the simple dialog.

What’s the code?

Listings A and B are the source code for the fancy expandable dialog box. You’ll notice that the only significant difference from the code for the simple dialog is the SetExpanded() method.

This method first checks to see if it needs to expand or contract the dialog. If it needs to expand the dialog, it first rolls the bottom panel down by increasing its Height property one pixel at a time from 1 to 253. Next it opens the doors by decreasing their Width property one pixel at a time from 245 to 0. SetExpanded() contracts the dialog by reversing this process.

To get everything to work smoothly, I call the method, Application->ProcessMessages(), for each step of the expansion or contraction. This serves a dual purpose. First, it allows the dialog a chance to redraw itself and keep everything looking nice. Second, it slows the process down just enough to make the animation look good.

As I did in the simple dialog code, I use the trick of setting the dialog’s AutoSize property to false and then to true to get it to resize to fit the changing bottom panel.

What’s left?

That’s all it takes to make a simple expanding dialog fancy. I hope I have expanded your horizons when it comes to creating dialog boxes.

There are a couple more things I’d like to mention.

First, it really isn’t good programming practice to use magic numbers. These are the numbers 253 and 245 I used for the Height and Width properties of the panels. I did this to keep this code simple. If you want to make the fancy dialog more versatile, you could calculate these numbers when you first create the dialog in your program.

Second, I could have derived TFancyExpandableDialog from TExpandableDialog. Changing the SetExpanded() method to a virtual method would do the trick. But, then I would have had to add the door panels completely in code and that seemed like too much trouble.

Listing A: FancyExpandDlg.h

#ifndef FancyExpandDlgH
#define FancyExpandDlgH

#include <Classes.hpp>
#include <Controls.hpp>
#include <StdCtrls.hpp>
#include <Forms.hpp>
#include <ExtCtrls.hpp>


class TFancyExpandableDialog
  : public TForm
{
  __published:
    TPanel *Panel1;
    TButton *OKBtn;
    TButton *CancelBtn;
    TButton *DetailsBtn;
    TPanel *Panel2;
    TPanel *Panel3;
    TPanel *Panel4;
    void __fastcall DetailsBtnClick(
      TObject *Sender);

  public:
    __fastcall TFancyExpandableDialog(
       TComponent* Owner);

    __property bool Expanded =
      {read = expanded,
       write = SetExpanded};

  private:
    void SetExpanded(bool expand);

    bool expanded;
};


extern PACKAGE 
  TFancyExpandableDialog *FancyExpandableDialog;


#endif   // FancyExpandDlgH

Listing B: FancyExpandDlg.cpp

#include <vcl.h>
#pragma hdrstop

#include "FancyExpandDlg.h"

#pragma package(smart_init)
#pragma resource "*.dfm"

TfancyExpandableDialog *FancyExpandableDialog;


__fastcall 
TfancyExpandableDialog::TFancyExpandableDialog(
  TComponent* Owner) : TForm(Owner)
{
  expanded = true;
  Expanded = false;
}

void __fastcall 
TFancyExpandableDialog::DetailsBtnClick(
  TObject *Sender)
{
  Expanded = !Expanded;
}

void TFancyExpandableDialog::SetExpanded(
  bool expand)
{
  if (expanded == expand) return;

  if (expanded)
  {
    for (int i = 1; i <= 245; i++)
    {
      Panel3->Width = i;
      Panel4->Width = i;
      Application->ProcessMessages();
    }
    for (int i = 253; i >= 0; i--)
    {
      Panel2->Height = i;
      Application->ProcessMessages();
    }
    AutoSize = false;
    AutoSize = true;
  }
  else
  {
    for (int i = 1; i <= 253; i++)
    {
      Panel2->Height = i;
      Application->ProcessMessages();
    }
    for (int i = 245; i >= 0; i--)
    {
      Panel3->Width = i;
      Panel4->Width = i;
      Application->ProcessMessages();
    }
  }

  expanded = expand;

  DetailsBtn->Caption = expanded
    ? "&Details <<" : "&Details >>";
}