Connect a list container to a tree/list control | CodeGuru

Connect a list container to a tree/list control

.   Most user interface oriented applications need a container of some kind (for example, list) and a visual representation of this container in a tree or list control. When a programmer modifies an item in a container, it must also update this specific item in a tree/list control. Vice versa, if a user of […]

Written By
CodeGuru Staff
CodeGuru Staff
Aug 6, 1998
5 minute read
CodeGuru content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More

.

 

Most user interface oriented applications need a container of some
kind (for example, list) and a visual representation of this container
in a tree or list control. When a programmer modifies an item in a container,
it must also update this specific item in a tree/list control. Vice versa,
if a user of the program modifies the contents of the item, programmer
must also update the item contents in a container.

As an example, I will discuss tree control since it is more complex.
Each level in a tree control can be (and usually is) represented with a
different data structure or class.

Using a structure for a node item

Each structure representing a node item should be declared in such a
way that its first data member is an enum type. Enum is declared separatelly
and identifies different type of structures which are used in a tree control.

enum treeItems {

    tiVehicleGroup,   
// Planes, trucks, cars, ships etc.


    tiVehicle,        
// Toyota, Renault, Mercedes, Chrysler, etc.


    tiPart            
// Windshield, Wheels, Tyres, Engine, etc.


};

struct TVehicleGroup {

    int Id;           
// Initialized to tiVehicleGroup


    CString Name;

    …..

    void *Handle;

};

struct TVehicle {

    int Id;           
// Initialized to tiVehicle


    CString Name;

    ……

    TVehicleGroup *Owner;

    void *Handle;

};

These structures are organized into one or more containers. When populating
a tree control, Name is used for item visual representation while a pointer
to a data structure is associated with a tree item: Handle is initialized
with a HTREEITEM handle returned from InsertItem function. It is declared
as a pointer to void instead of HTREEITEM in order to allow a structure
to be inserted in a tree or list control.

void TFoo::Foo(CTreeCtrl& tc, TVehicleGroup
*ptr)


{

    HTREEITEM handle = tc.InsertItem(ptr->Name);

    ptr->Handle = (void*)handle;

    tc.SetItemData(handle,(DWORD)ptr);

}

void TFoo::Foo(CTreeCtrl& tc, TVehicle
*ptr)


{

    HTREEITEM handle = tc.InsertItem(ptr->Name,
(HTREEITEM)ptr->Owner->Handle);


    ptr->Handle = (void*)handle;

    tc.SetItemData(handle,(DWORD)ptr);

}

 

Getting a pointer to a data structure given a handle to a tree item
is straightforward:

HTREEITEM handle = tc.GetSelectedItem();

TVehicle *ptr = (TVehicle*)tc.GetItemData(handle);

switch (ptr->Id) {

    case tiVehicleGroup:

       
{


           
TVehicleGroup *vg = (TVehicleGroup*)ptr;


           
// Do something


       
}


       
break;


    case tiVehicle:

       
{


           
TVehicle *vg = (TVehicleGroup*)ptr;


           
// Do something


       
}


       
break;


    case tiPart:

       
….


       
break;


};

I can safely test an Id member of an unknown structure only if it is
assunmed that all structures associated with tree items have an Id as a
first data member. After detecting the exact type, I can safelly access
all its data members.

 

Using classes for node items

With classes it is not possible to rely on an in-memory representation
of the class as I did with structures. Different approach is needed. First,
declare a base class for all classes whose instances will be associated
with tree items. This class contains all common data (name of tree item,
handle to tree item etc.).

class TBaseForTreeItem : public CObject {

    public:

       
CString Name;


       
void *Handle;


       
TBaseForTreeItem *Parent;


       
// ….


};

Then, concrete classes are declare using previous class as a base class.

class TVehicleGroup : public TBaseForTreeItem
{


    ….

};

class TVehicle : public TBaseForTreeItem {

    ….

};

Inserting an item into the tree control is the same as previously described.
The difference is in getting the pointer to the object in a container.
For this, we can use several different techniques:



  • If all classes use a DECLARE_SERIAL or DECLARE_DYNAMIC macro, then rtti
    is enabled and we can use IsKindOf to get to the right object.
  • HTREEITEM handle = tc.GetSelectedItem();

    TBaseForTreeItem *obj = (TBaseForTreeItem*)tc.GetItemData(handle);

    if (obj->IsKindOf(RUNTIME_CLASS(TVehicleGroup))
    {


        TVehicleGroup *vg = (TVehicleGroup*)obj;

        // Do something

    } else if (obj->IsKindOf(RUNTIME_CLASS(TVehicle))
    {


        TVehicle *v = (TVehicle*)obj;

        // Do something

    }

     


  • If rtti is enabled but we don’t use DECLARE_SERIAL or DECLARE_DYNAMIC macros,
    we can use a C++ defined dynamic_cast macro to get to the right object.
  • HTREEITEM handle = tc.GetSelectedItem();

    TBaseForTreeItem *obj = (TBaseForTreeItem*)tc.GetItemData(handle);

    TVehicleGroup *vg = dynamic_cast<TVehicleGroup*>(obj);

    if (vg != NULL) {

        // Do something

    } else {

        TVehicle *v = dynamic_cast<TVehicle*>(obj);

        if (v != NULL) {

           
    // Do something


        }

    }

     


  • If rtti is not enabled, we can rely on C++ inheritance and do the following:




    • Define an enum treeItem { tiBase, tiVehicleGroup,
      tiVehicle, … }
      .

    • Add a virtual int GetId(void) member
      function to a TBaseForTreeItem and all derived classes.

    • Implement this function in each class and return a different enum constant
      (based on a class type).


    With this modifications, it is possible to get the correct pointer to an
    object associated with a tree item using the following:

    HTREEITEM handle = tc.GetSelectedItem();

    TBaseForTreeItem *obj = (TBaseForTreeItem*)tc.GetItemData(handle);

    switch (obj->GetId()) {

        case tiVehicleGroup:

           
    {


               
    TVehicleGroup *vg = (TVehicleGroup*)obj;


               
    // Do something


           
    }


           
    break;


        case tiVehicle:

           
    {


               
    TVehicle *v = (TVehicle*)obj;


               
    // Do something


           
    }


           
    break;


    }


Conclusion

This technique can be used with all controls that allow a programmer
to associate a DWORD with a control item (list boxes, combo boxes, listview
etc.).

CodeGuru Logo

CodeGuru covers topics related to Microsoft-related software development, mobile development, database management, and web application programming. In addition to tutorials and how-tos that teach programmers how to code in Microsoft-related languages and frameworks like C# and .Net, we also publish articles on software development tools, the latest in developer news, and advice for project managers. Cloud services such as Microsoft Azure and database options including SQL Server and MSSQL are also frequently covered.

Property of TechnologyAdvice. © 2026 TechnologyAdvice. All Rights Reserved

Advertiser Disclosure: Some of the products that appear on this site are from companies from which TechnologyAdvice receives compensation. This compensation may impact how and where products appear on this site including, for example, the order in which they appear. TechnologyAdvice does not include all companies or all types of products available in the marketplace.