Dialog Switcher Using a Tree Control

Monday Nov 19th 2001 by Andrew Walker
Share:

So you want to use the new style of dialog where you pick an option from a tree on the left and the appropriate form shows up on the right. This article shows you were to start and how to accomplish this!

Environment: Visual C++ 6

So you want to use the new style of dialog where you pick an option from a tree on the left and the appropriate form shows up on the right, but you have no idea where to start.

The easiest way to set up the right side is to create a dialog for each form that you might use there. The trick is making it seamless with the overall dialog, and switching out the dialogs (the forms) that you create for that right-hand side.

Step 1: Create the Main Dialog.

Easy enough. Use the Resource Editor to create the dialog, and stretch it out a bit. Add a Tree View control to the left-hand side, making it tall and skinny so that it's readable but there's plenty of room for whatever is going to go on the right. Use Class Wizard to add a Member Variable for the control.

Step 2: Create the Right Side forms.

In the Resource Editor, select Insert>>Resource (or hit ctrl-R), select Dialog, and click New. Design as you please. Run Class Wizard (ctrl-W) to create a class for the dialog. At some point set these properties on the dialog itself:

  • Style must be Child (this is on the Styles tab)
  • Border (Styles tab again) should be none, but you can play with this. Keep in mind that if you do give it a border, you'll probably want to make sure all the dialogs you create for the right side are the same size so that the border doesn't shift about.
  • Speaking of size, you'll want to size the right-hand dialogs carefully so that they'll fit in the Main Dialog with room left over for the tree control. There are rulers at the top and left when designing dialogs to help you accomplish this.
Create as many right-side dialogs as you want.

Step 3: Initialize the dialogs

Ah, here we get to some code! In the Dlg class for your application, add include's for each of your right-side dialog classes. For ease (and because it's what they are), I'm going to call those Child Dialogs. Add a member variable to your Dlg header for each of your Child Dialogs. In the OnInitDialog function of your Dlg class, Create and Hide the Child Dialogs:

// Create the Child Dialogs
m_dlgOption1.Create(IDD_DIALOG_OPTION1, NULL);
m_dlgOption1.ShowWindow(SW_HIDE);
m_dlgOption2.Create(IDD_DIALOG_OPTION2, NULL);
m_dlgOption2.ShowWindow(SW_HIDE);
m_iActiveOption = 1;

// Create the tree.
HTREEITEM hItem;
hItem = m_tMyTree.InsertItem("Option 1", 
                             TVI_ROOT,
                             TVI_LAST);
m_tMyTree.SelectItem(hItem);
m_hCurrentItem = hItem;
m_tMyTree.InsertItem("Option 2", 
                     TVI_ROOT,
                     TVI_LAST);
m_tMyTree.SetFocus();

return FALSE;  // returning FALSE so the focus is
               //   set to the tree.
}

Note that I have also created a member variable m_hCurrentItem to track the currently selected item. This is of type HTREEITEM.

Step 4: Handle a Change In The Tree's Selection

Using Class Wizard, create a handler for your tree control to handle the TVN_SELCHANGED message. The function should look something like this:

void CDialogSwitcherByTreeDlg::OnSelchangedTree1(NMHDR* pNMHDR,
                                                 LRESULT* pResult) 
{
  NM_TREEVIEW*  pNMTreeView = (NM_TREEVIEW*)pNMHDR;
  HTREEITEM     hItem;
  CString       szOptionChosen;

  hItem = m_tMyTree.GetSelectedItem(); // Get a Handle to the 
                                       // newly selected item.
  m_hCurrentItem = hItem;  // Set the current item to the one
                          // just selected.
  szOptionChosen = m_tMyTree.GetItemText(hItem); // Get the text of 
                                                 // the chosen option.
  if (szOptionChosen == "Option 1") // If option 1 was chosen...
  {
    m_iActiveOption = 1;  // Set the active option to 1, 
                          //   hide option 2, repaint.
    m_dlgOption2.ShowWindow(SW_HIDE);
    OnPaint();
  }
  else if (szOptionChosen == "Option 2") // If option 2 chosen...
  {
    m_iActiveOption = 2;  // Set the active option to 2, 
                          //  hide option 1, repaint.
    m_dlgOption1.ShowWindow(SW_HIDE);
    OnPaint();
  }

  *pResult = 0;
}

Of course, my example here uses only 2 options and it's pretty simple to set up an if-else if structure. For more than a few options you might want to get creative and figure a more efficient way of doing changing the current selection.

Notice that I call OnPaint() as soon as my option is chosen. This is because I have put some code into the OnPaint() function to complete the transition from one Child Dialog to another:

RECT  rPositionMain;   // Main Dialog rectangle
RECT  rPositionChild;  // Child Dialog rectangle
int   iXDiff = 0;      // Left-Right change
int   iYDiff = 0;      // Up-Down change

GetWindowRect(&rPositionMain); // Get the rectangle 
                               // the dialog occupies
switch (m_iActiveOption) // Based on the button pushed...
{
case 1:
  m_dlgOption1.GetWindowRect(&rPositionChild); // Get child
                                                   // rectangle
  if (rPositionChild.top != 0) // If top side of child
                            // is not correctly positioned...
  {
    iYDiff = 0 - rPositionChild.top; // ...move top and bottom
                               // sides of child appropriately
    rPositionChild.top += iYDiff;
    rPositionChild.bottom += iYDiff;
  }
  if (rPositionChild.left != 150) // If left side of child
                         // is not correctly positioned...
  {
    iXDiff = 150 - rPositionChild.left; // ...move left and right
                                  // sides of child appropriately
    rPositionChild.left += iXDiff;
    rPositionChild.right += iXDiff;
  }
  if (iXDiff != 0 || iYDiff != 0)  // If we moved  child...
    m_dlgOption1.MoveWindow(&rPositionChild,
                            TRUE); // ...make the move!

  m_dlgOption1.ShowWindow(SW_SHOW); // Show the child dialog
  break;
case 2:
  m_dlgOption2.GetWindowRect(&rPositionChild); // Get child
                                                   // rectangle
  if (rPositionChild.top != 0) // If top side of child is
                               // not correctly positioned...
  {
    iYDiff = 0 - rPositionChild.top; // ...move top and bottom
                               // sides of child appropriately
    rPositionChild.top += iYDiff;
    rPositionChild.bottom += iYDiff;
  }
  if (rPositionChild.left != 150) // If left side of child 
                            // is not correctly positioned...
  {
    iXDiff = 150 - rPositionChild.left; // ...move left and right
                                    // sides of child appropriately
    rPositionChild.left += iXDiff;
    rPositionChild.right += iXDiff;
  }
  if (iXDiff != 0 || iYDiff != 0)  // If we moved the child...
    m_dlgOption2.MoveWindow(&rPositionChild,
                            TRUE);  // ...make the move!
  m_dlgOption2.ShowWindow(SW_SHOW); // Show the child dialog
  break;
}

// Bold the current item, turn Bold off for the rest
//  of the tree items.
HTREEITEM hItem = m_tMyTree.GetFirstVisibleItem();
while (hItem != NULL)
{
  if (hItem == m_hCurrentItem)
    m_tMyTree.SetItemState( hItem, TVIS_BOLD, TVIS_BOLD );
  else
    m_tMyTree.SetItemState( hItem, 0, TVIS_BOLD );
  hItem = m_tMyTree.GetNextVisibleItem(hItem);
}

Again, if you have more than a very few options you might want to work out a more graceful way of parsing them besides if-else if.

The important part of what I do in OnPaint() is moving the Child Dialog around. Since we set the style of each of the Child Dialogs to be Child, the coordinates used will be relative to their parent window, which is our main dialog. Initially, these coordinates are going to be wrong (Windows sets them relative to the screen), but after one trip through OnPaint() they should be fine. Theoretically, the window positions should only be adjusted once, but it's better to be safe than sorry.

Note that I offset the left side of the Child Dialogs by 150 pixels to allow room for the tree control. Depending on the width of your tree control, you may have to adjust this.



So there you have it: A tree control that changes the forms that show up on the dialog. Since each of the Child Dialogs are their own classes, the Main Dialog doesn't have to worry about what happens in them.

Downloads

Download demo project - 14 Kb
Share:
Home
Mobile Site | Full Site
Copyright 2017 © QuinStreet Inc. All Rights Reserved