The Wayback Machine - https://web.archive.org/web/20121018230748/http://www.codeguru.com/cpp/controls/treeview/misc-advanced/article.php/c727/A-generic-Tree-Property-Sheet-control.htm

A generic Tree Property Sheet control

CTreePropertySheet

This class implements a generic tree property control (see picture above).


Its features are:
- Supports variable width tree control
- Autosizing depending on the size of the dialogs contained
- As this version is now based upon the CPropertySheet/ CPropertyPage model, all features of these are available.
- Various styles(for example, like Netscape Communicator's property sheet)
- Extensibility: Allows to integrate user defined controls (like buttons or static text boxes) and easily position them.
- Now supports modeless property sheets as well.


How to implement a CTreePropertySheet in your application:

Step 1.
    Insert "TreePropertySheet.cpp" to your application.
Step 2.
    Insert an

    #include "TreePropertySheet.h"

    in the class implementation file in which you want to use the CTreePropertySheet.
Step 3.
    Define a function which invokes the property sheet, for example:
    
    void CMyView::OnPropertySheet()
    {
    	// TODO: ...
    }
    
Step 4.
    Create a CTreePropertySheet object in the handler and the property pages you want to use with CTreePropertySheet:
    
    ...
    CTreePropertySheet tpsSheet;
    CGeneralPrefsPage cGeneralPrefs;
    COtherPrefsPage cOtherPrefs;
    // All dialogs contained in the property sheet must (now) be allocated from the stack.

    Caution: To make it work, the dialogs you want to include must have the following style:
    - Style: 'Child'
    - Border: None
    - Caption: Yes
    - Set the caption to the text you wish to appear when the page is selected.
    - All other options must be unchecked.

Step 5.
    Add the dialogs to the tree property sheet:
    
    ...
    tpsSheet.AddPage(tps_item_node,&cGeneralPrefs);
    tpsSheet.AddPage(tps_item_node,&cOtherPrefs);
    
    You have to specify the resource ID when adding the dialog. The text argument is the title shown up in the tree control, 'tps_item_node' tells that this is a simple node in the tree.
    (See below for more information)
Step 6.
    Start the property sheet:
    
    ...
    int nRetCode=tpsSheet.DoModal();
    ...
    
    Optionally, you can set a special pre-defined style before DoModal():
    
    ...
    tpsSheet.SetLikeNetscape();
    int nRetCode=tpsSheet.DoModal();
    ...
    


Remarks

To set up the tree structure, there are three different attributes for 'AddPage()':

tps_item_branch This item has sub-items. All following items are one level below this item, until a 'tps_item_endbranch' is found.
tps_item_node This item is a simple node in the tree.
tps_item_endbranch This item is the last item of the current sub-branch(which was initiated with an tps_item_branch). All following items are on the same level as the corresponding 'tps_item_branch' entry.


To make it clearer, please take a look at the table below:

(tree control contents) (code)
+ Main preferences AddPage(tps_item_branch, &cMainPrefsPage);
    -; Directories AddPage(tps_item_node, &cDirectoriesPage);
    -; User information AddPage(tps_item_node, &cUserPage);
    -; Plugins AddPage(tps_item_endbranch, &cPluginPage);
- Security AddPage(tps_item_node, &cSecurityPage);

If you want to enhance the CTreePropertySheet by own controls or buttons, this can be done using InsertExtraControl() / InsertExtraSpace().
For more information about these functions, please take a look into the HTML help file(see below) and into the example project.

Downloads

Download help file - 29 KB

Download demo project - 52 KB

Download source - 14 KB

These files can also be downloaded at http://members.xoom.com/softserv/tps_index.html

IT Offers

Comments

  • SelChanged vs. SelChanging

    Posted by Legacy on 05/17/2000 12:00am

    Originally posted by: Kent Murray

    It has been my experience that as written this class fails to operate correctly when a category selection fails due to data validation problems during the dialog data exchange that is triggered by the page change. This might not be obvious normally, but I modified the tree control creation to include the always show selection flag.

    When the validation fails the page is not changed, but the tree control still indicates the selection to be the that of the page which you tried to select. To correct this behavior I handle the selection _changing_ message rather than the selection _changed_ message. In the selection changing message handler I set the result value (second argument) to be the negation of the value returned from SetActivePage(). This will keep the tree control selection from changing when the active page does not change.

    For example:

    BEGIN_MESSAGE_MAP(CTreePropertySheet, CPropertySheet)
    ON_NOTIFY(TVN_SELCHANGING,ID_TREECTRL,OnSelChanging)
    END_MESSAGE_MAP()

    void CTreePropertySheet::OnSelChanging(
    NMHDR* pNotifyStruct,
    RESULT* pResult)
    {
    NMTREEVIEW *pNotify=(NMTREEVIEW*)pNotifyStruct;
    DWORD dwPage = m_cTreeCtrl.GetItemData(pNotify->itemNew.hItem);
    LockWindowUpdate();
    (*pResult) = !SetActivePage(dwPage);
    UnlockWindowUpdate();

    // Prevent losing the focus when invoked by keyboard
    if(!(*pResult) && pNotify->action==TVC_BYKEYBOARD) {
    m_cTreeCtrl.SetFocus();
    }
    InvalidateRect(&m;_rcCaptionBar,FALSE);
    }

    [Note: I downloaded the class source directly from the author's homepage, and I have not compared it against the source available on CodeGuru.]

    Reply
  • CTreePropertySheet fix for NT 5

    Posted by Legacy on 09/10/1999 12:00am

    Originally posted by: Mario Contestabile

    Add
    BEGIN_MESSAGE_MAP(CTreePropertySheet, CPropertySheet)
    ON_NOTIFY(TVN_SELCHANGEDW,ID_TREECTRL,OnSelChanged)
    ...
    END_MESSAGE_MAP()

    in treepropertysheet.cpp

    Reply
  • page change error solved

    Posted by Legacy on 08/11/1999 12:00am

    Originally posted by: Andr� Gleichner

    The error occurs because the treeview control is in UNICODE mode (somebody knows why?) although it's an ANSI build and therefore sends its WM_NOTIFY codes also as UNICODE messages. Here it sends a TVN_SELCHANGEDW instead of TVN_SELCHANGEDA. It seems to be a very common problem as it shows up in several newsgroups with some variations (with ListCtrls, sends only ANSI codes in UNICODE builds, etc). Most people recommend to handle both the ANSI and the UNICODE code (which works here) but that makes it more complicated than necessary (all codes have to be doubled as well as some strings received from treectrl are useless/have to be converted).

    Simply add the following lines within the CTreePropertySheet::AddTreeView() function right after the CreateEx() call. This will switch the treectrl to the right mode.

    BOOL tIsUnicode;
    #ifdef UNICODE
    tIsUnicode = TreeView_SetUnicodeFormat(m_cTreeCtrl.m_hWnd, 1);
    #else
    tIsUnicode = TreeView_SetUnicodeFormat(m_cTreeCtrl.m_hWnd, 0);
    #endif // UNICODE

    I somewhere read that the call to InitCommonControls() has something to do with that odd behavior. Anybody there who knows details?

    Best regards
    Andr� Gleichner

    Reply
  • Same problem as Peter: dialogs doesn't update ...

    Posted by Legacy on 08/06/1999 12:00am

    Originally posted by: Raymond Pols

    Hoi,

    I have the same problem on an NT workstation. The dialog pages doesn't change when selecting them from the treeview.

    Any idea yet?

    Thanks.

    By the way: great work though!

    Reply
  • Propertysheet doesn't work

    Posted by Legacy on 07/28/1999 12:00am

    Originally posted by: Peter Hartmann

    Hello Alexander,

    I've compiled your Demo and testet it. The dialogs doesn't change. Nothing happens.

    Environment : NT 4.0 SP 4
    VS 6.0 SP 3

    Please have a look...

    Greetinx
    Peter :-)

    Reply
  • Comments

    Posted by Legacy on 07/02/1999 12:00am

    Originally posted by: Alexander Berthold

    I am sorry, but i had almost no time to review and update this page. I appreciate your comments, and i know the design of the tree property sheet control had many disadvantages. I'd been in some time pressure ... you know that, don't you ;)
    Now i have updated the whole thing a lot.
    I've completely redesigned the control, derived it from
    CPropertySheet and using the property sheet / property page design. It is now 100% compatible to windows property sheets and almost all of the methods are compatible to the old version.
    I've sent the update to codeguru today.
    To access the sources directly, click the link in the
    CTreePropertySheet page to my homepage.
    URL:

    http://members.xoom.com/softserv/tps_index.html

    Again, thanks for your time reviewing the code.

    Reply
  • Is there a way to use API-style dialogs?

    Posted by Legacy on 05/19/1999 12:00am

    Originally posted by: Ken Krakman

    The parameter to the Tree Property Sheet only uses a
    CDialog. I have an application that has 40 API-style
    dialog boxes that have code associated with them all
    (the dialog procs that handle messages). Is there a way to
    use this class and still re-use all that code?

    Thanks!

    Reply
  • TABbing problem with edit-style combo boxes

    Posted by Legacy on 02/02/1999 12:00am

    Originally posted by: Robert Clark

    Really great work!!

    I have been playing with the control and I have discovered a problem with edit-style combo-box control in a prop sheet when tabbing. When using the TAB key (TAB or Shift-TAB) from an edit-style combo-box the software freezes up. My guess is a problem with the child edit window, but I have not isolated it yet.

    Anyone have any ideas or experience with this problem?

    Reply
  • TABbing problem with edit-style combo boxes - Solved

    Posted by Legacy on 02/02/1999 12:00am

    Originally posted by: Robert Clark

    I traced the problem down in Debugger.	Now I know why ido not like for (;;) infinite loops, or any other
    infinite loop for that matter.	What was happening is that the EDIT child hwnd is passed to the control
    search function and it is never found as a valid dlg control (it's parent combobox is the control).  The
    function goes through all possibilities and continues in the infinite loop with NULL's (0) forever!!.
    
    

    I added another "infinite loop" with an out that will attempt to find the correct dlg control until it fails.

    added to cut down on HWND calculations:

    HWND hwndParent = m_pCurItem->pDialog->GetSafeHwnd();

    added this loop to find correct child dlg control if grandchild or greater is supplied:

    // make sure hwndCtl is no deeper than a child (Edit-child from combobox)
    for(;;) {
    if (::GetParent(hwndCtl) != hwndParent) {
    // hwndCtl is not a child
    hwndCtl = ::GetParent(hwndCtl);
    }
    else
    break;
    // if not found, fail
    if(hwndCtl == NULL)
    return NULL;
    }

    added safety catch in this loop which was locking up the program:

    // 1st: Find hwndCtl(100% secure as IsChild()==TRUE)(not exactly TRUE)
    for(;;) {
    if(hwndCur==hwndCtl) break;
    // if not found, fail
    if(hwndCtl == NULL)
    return NULL;
    hwndCur=::GetWindow(hwndCur,GW_HWNDNEXT);
    }


    //FULL FUNCTION SOURCE

    HWND CTreePropertySheet::SearchNextTabItemDialog(HWND hwndCtl,BOOL bPrevious)
    {
    if(m_pCurItem==NULL)
    return NULL;

    if(::IsChild(m_pCurItem->pDialog->GetSafeHwnd(),hwndCtl)==FALSE)
    return NULL;

    // manually search for the next tab item
    // (GetNextDlgTabItem() loops thru the items, but we need
    // the end ...)
    HWND hwndFirst=::GetWindow(m_pCurItem->pDialog->GetSafeHwnd(),GW_CHILD);
    HWND hwndCur = hwndFirst;
    HWND hwndNextTab = NULL;
    HWND hwndParent = m_pCurItem->pDialog->GetSafeHwnd();

    // make sure hwndCtl is no deeper than a child (Edit-child from combobox)
    for(;;) {
    if (::GetParent(hwndCtl) != hwndParent) {
    // hwndCtl is not a child
    hwndCtl = ::GetParent(hwndCtl);
    }
    else
    break;
    // if not found, fail
    if(hwndCtl == NULL)
    return NULL;
    }
    // 1st: Find hwndCtl(100% secure as IsChild()==TRUE)(not exactly TRUE)
    for(;;) {
    if(hwndCur==hwndCtl) break;
    // if not found, fail
    if(hwndCtl == NULL)
    return NULL;
    hwndCur=::GetWindow(hwndCur,GW_HWNDNEXT);
    }

    // 2nd: Find next control with WS_TABSTOP set
    if(bPrevious) {
    for(;;) {
    hwndCur=::GetWindow(hwndCur,GW_HWNDPREV);
    if(hwndCur==NULL) break;
    if( (::GetWindowLong(hwndCur,GWL_STYLE)&WS_TABSTOP)==WS_TABSTOP) {
    hwndNextTab=hwndCur;
    break;
    }
    }
    } else
    {
    for(;;) {
    hwndCur=::GetWindow(hwndCur,GW_HWNDNEXT);
    if(hwndCur==NULL) break;
    if( (::GetWindowLong(hwndCur,GWL_STYLE)&WS_TABSTOP)==WS_TABSTOP) {
    hwndNextTab=hwndCur;
    break;
    }
    }
    }

    return hwndNextTab;
    }

    Reply
  • Update all pages on Ok, rather than just the currently active one.

    Posted by Legacy on 01/05/1999 12:00am

    Originally posted by: Steve Kemp

    Hi,
    
    Well first of all thanks for the code!

    I've used it in some of my applications for replacing large
    property sheets, which start looking ugly and get tricky to
    navigate when the number of pages start to grow.

    The main problem with the current implementation from the property
    sheet point of view is that the OnOk method of the dialogs only gets
    called for the currently active dialog.
    In property sheets the OnOk, (or OnApply method), gets called for
    each page, so if you replace the sheet with the dialog, and the
    user changes settings on multiple pages the only dialog that knows
    about them is the current one.

    I've changed the implementation to call the OnOk method of
    each dialog, that means that each seperate page can update
    any relevant settings.

    This could be set with a flag, so that the programmer can choose
    whether all pages get updated, or only the currently active one -
    I've chosen to hardcode it as this is the only method that I use.

    Below are the updated methods.

    Steve
    ---
    http://www.tardis.ed.ac.uk/~skx/win/ - Freeware programs for Windows

    //
    // Call OnOk methods of each dialog when the user clicks on OK.
    //
    void CTreePropertySheet::OnOk()
    {
    EnableWindow(FALSE);
    EndModalLoop(IDOK);

    ///
    int i;
    // Size of dialog array
    int nSize = m_aDlgItems.GetSize();

    // Go through all dialogs.
    for(i=0;i<nSize;i++)
    {
    CItem &item=m_aDlgItems[i];
    // Make sure dialog is non-null
    if(item.pDialog!=NULL)
    {
    HWND hwndOkBtn = item.pDialog->GetDlgItem(IDOK)->GetSafeHwnd();
    item.pDialog->SendMessage(msgCmdPass,MAKELONG(IDOK,BN_CLICKED),(LPARAM)hwndOkBtn);
    }
    }
    }

    //
    // Call the OnCancel method of each dialog when the user clicks on
    // cancel.
    //
    void CTreePropertySheet::OnCancel()
    {
    EnableWindow(FALSE);
    EndModalLoop(IDCANCEL);

    ///
    int i;
    // Size of dialog array
    int nSize = m_aDlgItems.GetSize();

    // Go through all dialogs.
    for(i=0;i<nSize;i++)
    {
    CItem &item=m_aDlgItems[i];
    // Make sure dialog is non-null
    if(item.pDialog!=NULL)
    {
    HWND hwndCancelBtn= item.pDialog->GetDlgItem(IDCANCEL)->GetSafeHwnd();
    item.pDialog->SendMessage(msgCmdPass,MAKELONG(IDCANCEL,BN_CLICKED),(LPARAM)hwndCancelBtn);
    }
    }
    }

    Reply

Whitepapers and More

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds