In a recent project that uses a visual framework, I needed an overlapped dialog that is always present and whose contents (child controls and their layout) are changing depending on the currently selected view or a tab. Instead of creating several independent dialogs and choosing which one will be visible, I designed a so-called card dialog.
Card dialog is an empty standard dialog box. It contains a list of cards (child dialogs). Depending on the application, one of the cards is visible and enabled. In addition, card dialog will resize itself to the size of the card. Several virtual functions enable the programmer to initialize the card when it is created / destroyed or when it is (de)activated.
Step 1:
Create an empty dialog resource (this will be a card dialog). The size is not important. It should be empty (no controls) since its client area will be covered with one of the cards. Create a corresponding class using ClassWizard. If you intend to use it as a modal dialog then create OnInitDialog
handler.
Step 2:
Create a separate dialog resource for each card. Dialog resource should have the following styles:
- Child dialog
- No border
- No caption
- Disabled
- Not visible
Place any controls that you need on each dialog. Using ClassWizard, create a corresponding class. This class must be derived from TCard
. TCard
class does not have any special implementation. Its only intention is to prevent closing of the card when the user presses Enter or Escape (TCard::OnOK()
and TCard::OnCancel()
have empty implementation).
Step 3:
Initialize the card dialog by adding the cards. Here is the sample (for a modeless card dialog):
m_CardDialog.Create(IDD_CARD_DIALOG, this);
m_CardDialog.AddCard(IDD_CARD_ONE);
m_CardDialog.AddCard(IDD_CARD_TWO);
m_CardDialog.AddCard(IDD_CARD_THREE);
m_CardDialog.ShowCard(IDD_CARD_THREE);
m_CardDialog.ShowWindow(SW_SHOW);
If TCardDialog::ShowCard()
is not called, card dialog will display the first card.
Previous sample uses TCardDialog::AddCard(UINT nId)
to add a card (nId
is a card resource ID). In this case, AddCard()
will call a virtual function TCardDialog::CreateCard(UINT nId)
that ASSERT
s. So, m_CardDialog
must be an instance of a class derived from TCardDialog
that implements the function CreateCard()
in the following manner:
CDialog *CCardDialog::CreateCard(UINT nId)
{
switch (nId) {
case IDD_CARD_ONE: return new CCardOne;
case IDD_CARD_TWO: return new CCardTwo;
....
}
return NULL;
}
The alternative solution is to use TCardDialog::AddCard(UINT nId, TCard *pDialog)
where pDialog
is an instance of a class derived from TCard
and created with new
. Actual dialog should not be created. Sample:
m_CardDialog.Create(IDD_CARD_DIALOG, this);
m_CardDialog.AddCard(IDD_CARD_ONE, new CCardOne);
m_CardDialog.AddCard(IDD_CARD_TWO, new CCardTwo);
m_CardDialog.AddCard(IDD_CARD_THREE, new CCardThree);
m_CardDialog.ShowCard(IDD_CARD_THREE);
m_CardDialog.ShowWindow(SW_SHOW);
In this case, m_CardDialog
can be an instance of TCardDialog
since virtual function CreateCard()
will not be called.
When a card (dialog) is created, TCardDialog
will call virtual function TCardDialog::OnCardCreated()
. Before the card is destroyed, TCardDialog
will call virtual function TCardDialog::OnCardDestroyed()
. These 2 virtual functions enable the derived class to perform initialization of the cards.
Step 4:
In order to switch the active card, call the following function:
m_CardDialog.ShowCard(IDD_CARD_TWO);
You can obtain a pointer to the currently active card with:
CDialog *pDialog = m_CardDialog.GetCard();
TCardDialog::ShowCard()
will call virtual function TCardDialog::CanShowCard()
to check whether it is allowed to change the active card. Once the active card is changed, TCardDialog
will call virtual function TCardDialog::OnShowCard()
first for deactivated card and then for activated one. These 2 virtual functions enable a derived class to perform additional tasks related to cards.
I hope that you will find this class useful.
Latest version may be downloaded from http://www.scasoftware.com/.