List VCD Properties

This chapter shows you how to retrieve all properties of a video capture device, using the new VCD property interface. In addition, it is shown how to access and manipulate a property using predefined IDs.

All properties that are retrieved from the video capture device are displayed in a TreeView. The TreeView was chosen, because it perfectly matches the organization of the properties, since they naturally form a tree structure. The root of the tree is the IVCDPropertyItems collection. The collection contains all IVCDPropertyItems that are provided by the current video capture device. Each IVCDPropertyItem has one or more IVCDPropertyElements, which in turn have one or more IVCDPropertyInterfaces, that are used to access the property.

The source code for this sample program is contained in the %TOPLEVEL%\Samples\VC\List VCDProperties sample source directory.

Setting up the Project

The sample program of this chapter is a dialog based application. To set up such a project, follow the steps as described in the FirstSteps chapter, except that this sample is a "Dialog based" type of application, instead of a "Single Document" type as described in the FirstSteps.

There are some classes in the samples\VC\Common directory, which are used to manipulate the properties. In order to use them, they must be added to the project. To do so, right mouse click on the project in the project explorer. Choose "New Folder..." and name the folder controls. The "File extension" field can be left open. Now right click on the folder that was just created and select "Add Files to Folder...". Browse to the samples\VC\Common directory and select all files that begin with "VCD". Click the "OK" button and the files will be added to the project.

Some bitmaps have to be included for the tree view. Select "Resource..." from the "Insert" menu. In the "Insert Resource" dialog select "Bitmap" and click on the "Import" button. In the dialog that follows, set the "Files of type" to "All Files (*.*)" and browse to the directory of this sample. Open the "res" subdirectory, select all bitmap files and click the "Import" button. Now give them appropriate IDs like IDB_AUTO and IDB_BUTTON.

Adding Functionality to the Application

Add two buttons to the dialog. Label them "Select Device..." and "Property Page" and give them the IDs "IDC_SELECT_DEVICE" and "IDC_SHOW_PAGE". Now, add a tree control and give it the ID "IDC_VCDTREE". The tree control is used to visualize the organization of the VCDProperties. Add a static text field and give it the ID "IDC_DISPLAY". Open the property dialog of the text field and check the "Border" property under the "Styles" tab. This control will be used to display the live video. Finally, add a group box and give it the ID "IDC_CTRL_FRAME". The appropriate controls to manipulate the properties will be placed inside this frame at runtime.

To call the dialog, open the file "VCD Property Tree.cpp" and change the code in the InitInstance method as follows:

CVCDPropertyTreeDlg dlg;
if( !dlg.selectDevice() )
{
    AfxMessageBox( TEXT("No device was selected.") );
    return FALSE;
}
m_pMainWnd = &dlg;
dlg.DoModal();
// Since the dialog has been closed, return FALSE so that we exit the
// application, rather than start the application's message pump.
return FALSE;

This ensures that the device selection dialog is called before the main dialog and ensures that there is a valid device, otherwise the application shows a message box and terminate.

In order to add functionality to the dialog class, open the file "VCD Property TreeDlg.h" and include the file "VCDPropertyCtrl.h":

#include "../Common/VCDPropertyCtrl.h"

Also insert some using statements directly above the class definition:

using DShowLib::tIVCDPropertyItemsPtr;
using DShowLib::tIVCDPropertyItemPtr;
using DShowLib::tIVCDPropertyElementPtr;
using DShowLib::tIVCDPropertyInterfacePtr;

We need a little container class, in which the interface path is stored. An interface path consists of an itemID, an elementID and an interfaceID. The class is implemented as follows:

class CInterfacePath
{
public:
    GUID itemID;
    GUID elementID;
    GUID interfaceID;
};

Now add a public method selectDevice and some protected methods and variables to the CVCDPropertyTreeDlg class:

public:
    bool    selectDevice();

protected:
    HICON m_hIcon;
    DShowLib::Grabber    m_Grabber;
    CImageList            m_ImageList;    // Contains graphical elements for the tree view.
    void QueryVCDProperties();
    void QueryVCDPropertyItems( tIVCDPropertyItemsPtr pItems, HTREEITEM hRoot );
    void QueryVCDPropertyElements( tIVCDPropertyItemPtr pItem, HTREEITEM hItem );
    void QueryVCDPropertyInterfaces( tIVCDPropertyElementPtr pElem, HTREEITEM hElem );

    void ListAllPropertyItems();
    CRect DpiAdjustedRect( int left, int top, int right, int bottom );
    smart_ptr< CWnd >    m_pCurrentControl;
    smart_ptr< CWnd >    m_pBuddy;
    std::vector< CInterfacePath > m_InterfaceVector;

Now edit the file "VCD Property TreeDlg.cpp" to implement the methods. First, include the interface classes for the properties and declare the namespace DShowLib:

#include "../Common/VCDRangeSlider.h"
#include "../Common/VCDSwitchCheck.h"
#include "../Common/VCDButtonButton.h"
#include "../Common/VCDMapStringsCombo.h"
#include "../Common/VCDAbsValSlider.h"
using namespace DShowLib;

The graphical elements for the tree view, are stored in the image list at construction time of the dialog. Therefore, extend the standard constructor as follows:

CVCDPropertyTreeDlg::CVCDPropertyTreeDlg(CWnd* pParent /*=NULL*/)
    : CDialog(CVCDPropertyTreeDlg::IDD, pParent)
{
    //{{AFX_DATA_INIT(CVCDPropertyTreeDlg)
        // NOTE: the ClassWizard will add member initialization here
    //}}AFX_DATA_INIT
    // Note that LoadIcon does not require a subsequent DestroyIcon in Win32
    m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
    // Create the image list for the tree view and load images
    m_ImageList.Create( 16, 16, ILC_COLOR4, 0, 7 );
    CBitmap bm;
    bm.LoadBitmap( IDB_ITEM );
    m_ImageList.Add( &bm, RGB(0,0,0) );
    bm.Detach();
    bm.LoadBitmap( IDB_VALUE );
    m_ImageList.Add( &bm, RGB(0,0,0) );
    bm.Detach();
    bm.LoadBitmap( IDB_AUTO );
    m_ImageList.Add( &bm, RGB(0,0,0) );
    bm.Detach();
    bm.LoadBitmap( IDB_RANGE );
    m_ImageList.Add( &bm, RGB(0,0,0) );
    bm.Detach();
    bm.LoadBitmap( IDB_SWITCH );
    m_ImageList.Add( &bm, RGB(0,0,0) );
    bm.Detach();
    bm.LoadBitmap( IDB_BUTTON );
    m_ImageList.Add( &bm, RGB(0,0,0) );
    bm.Detach();
    bm.LoadBitmap( IDB_MAPSTRINGS );
    m_ImageList.Add( &bm, RGB(0,0,0) );
    bm.Detach();
}

This fills the image list with the graphical elements, previously added to the resources.

On the OnInitDialog event, the grabber and the tree view need to be set up. Add the following code to the OnInitDialog method to do so:

m_Grabber.setHWND( GetDlgItem( IDC_DISPLAY )->GetSafeHwnd() );
m_Grabber.startLive();
CTreeCtrl* pTree = (CTreeCtrl*)GetDlgItem( IDC_VCDTREE );
pTree->SetImageList( &m_ImageList, TVSIL_NORMAL );
// Print the property tree to debug out
ListAllPropertyItems();
QueryVCDProperties();

The code above first sets the HWND of the grabber to the static text control, so that it displays the live video. The image list is bound to the tree view via SetImageList, so that the tree view control has access to the graphical elements. Finally, ListAllPropertyItems and QueryVCDProperties are called. ListAllPropertyItems prints the tree structure of all supported properties to the debug output window. The debug output shows you the pure organization of the VCDProperties without taking care of other aspects like controls, while QueryVCDProperties builds up the tree view. Please note that you have to run the program in debug mode in order to see the property tree in the debug output window.

The public method selectDevice, which is called at startup, shows the device selection dialog. It is implemented as follows:

bool CVCDPropertyTreeDlg::selectDevice()
{
    m_Grabber.showDevicePage( m_hWnd );
    return m_Grabber.isDevValid();
}

Now use the class wizard to insert the click events for the two buttons "IDC_SELECT_DEVICE" and "IDC_SHOW_PAGE". A click on the buttons should bring up the appropriate dialog. The implementation of the click event for the "IDC_SHOW_PAGE" button is simple: you just have to call the property dialog:

void CVCDPropertyTreeDlg::OnShowPage()
{
    // Show the property page
    m_Grabber.showVCDPropertyPage( m_hWnd );
}

The implementation of the click event for the "IDC_SELECT_DEVICE" event needs some more lines of code, because it must be checked, if the live mode is running. Additionally, the tree view has to be be updated after a new device has been selected. The code to do this, is as follows:

void CVCDPropertyTreeDlg::OnSelectDevice()
{
    // Live mode has to be stopped before showing the device page
    m_Grabber.stopLive();
    // Show device page
    m_Grabber.showDevicePage( m_hWnd );
    // start live mode if a valid device is selected
    if( m_Grabber.isDevValid() ) {
        m_Grabber.startLive();
    }
    // Print the property tree to debug out
    ListAllPropertyItems();
    // Re-Build the tree
    QueryVCDProperties();
}

ListAllPropertyItems prints the property tree to debug out. it is implemented as follows:

void CVCDPropertyTreeDlg::ListAllPropertyItems()
{
    OutputDebugString( TEXT("\n\nVCD Tree:\n\n") );
    // Get all property items
    tIVCDPropertyItemsPtr pVCDProperties = m_Grabber.getAvailableVCDProperties();
    if( pVCDProperties != 0 )
    {
        // Iterate through the items and print the names.
        tVCDPropertyItemArray itemArray = pVCDProperties->getIndex();
        for( tVCDPropertyItemArray::const_iterator itItem = itemArray.begin(); itItem != itemArray.end(); ++itItem )
        {
            // Print the item name
            OutputDebugString( CString( (*itItem)->getName().c_str() ) );
            OutputDebugString( TEXT("\n") );
            // Iterate through the elements and print the names.
            tVCDPropertyElementArray elemArray = (*itItem)->getElements();
            for( tVCDPropertyElementArray::iterator itElem = elemArray.begin(); itElem != elemArray.end(); ++itElem )
            {
                // Print the element name
                OutputDebugString( TEXT("   Element : ") );
                OutputDebugString( CString( (*itElem)->getName().c_str() ) );
                OutputDebugString( TEXT("\n") );
                // Iterate through the interfaces and print the names.
                tVCDPropertyInterfaceArray itfArray = (*itElem)->getInterfaces();
                for( tVCDPropertyInterfaceArray::const_iterator itItf = itfArray.begin(); itItf != itfArray.end(); ++itItf )
                {
                    // Check the interface type and print the name
                    OutputDebugString( TEXT("        Interface : ") );
                    GUID iid = (*itItf)->getInterfaceID();
                    if( iid == IID_IVCDAbsoluteValueProperty )
                    {
                        OutputDebugString( TEXT("Absolute Value\n") );
                    }
                    else if( iid == IID_IVCDMapStringsProperty )
                    {
                        OutputDebugString( TEXT("Mapstrings\n") );
                    }
                    else if( iid ==  IID_IVCDSwitchProperty )
                    {
                        OutputDebugString( TEXT("Switch\n") );
                    }
                    else if( iid == IID_IVCDButtonProperty )
                    {
                        OutputDebugString( TEXT("Button\n") );
                    }
                    else if( iid == IID_IVCDRangeProperty )
                    {
                        OutputDebugString( TEXT("Range\n") );
                    }
                }
            }
        }
    }
}

The three nested for loops in the code above reflect the tree structure of the VCDProperties. The first loop covers all available IVCDPropertyItems and prints their names to the debug output. For each item, the second loop prints out all available IVCDPropertyElements. The third loop finally prints out all types of interfaces that the element provides. Please note that the interfaces are sorted according to their level of detail. The most detailed one is the "AbsoluteValue" interface, followed by the "MapStrings" interface. "Switch" and "Button" are detailed interfaces providing simple functionality: toggle On/Off, trigger. The "Range" interface is the most generic one, but does not provide a meaning for the values that it represents. For example the "Brightness" range may be from 0 to 255. There is no way to tell, whether 0 or 255 is the maximum brightness value.

The method QueryVCDProperties initializes the tree view with the properties of the current video capture device:

void CVCDPropertyTreeDlg::QueryVCDProperties()
{
    CTreeCtrl* pTree = (CTreeCtrl*)GetDlgItem( IDC_VCDTREE );
    // Clear the tree
    pTree->DeleteAllItems();
    // Insert the root item
    HTREEITEM hRoot = pTree->InsertItem( TEXT("VCD Properties") );
    // Set item data to MAXDWORD, because this is not an interface.
    pTree->SetItemData( hRoot, MAXDWORD );
    tIVCDPropertyItemsPtr pVCDProperties = m_Grabber.getAvailableVCDProperties();
    if( pVCDProperties != NULL )
    {
        QueryVCDPropertyItems( pVCDProperties, hRoot );
    }
    // Make sure the tree is expanded
    pTree->Expand( hRoot, TVE_EXPAND );
}

The procedure above clears the tree view and creates a new root node. The root node is labeled "VCDPropertyItems". The procedure QueryVCDPropertyItems inserts all available property items. QueryVCDPropertyItems looks as follows:

void CVCDPropertyTreeDlg::QueryVCDPropertyItems( tIVCDPropertyItemsPtr pItems, HTREEITEM hRoot )
{
    CTreeCtrl* pTree = (CTreeCtrl*)GetDlgItem( IDC_VCDTREE );
    // Iterate through the items, insert them into the tree and query for their elements.
    tVCDPropertyItemArray itemArray = pItems->getIndex();
    for( tVCDPropertyItemArray::const_iterator i = itemArray.begin(); i != itemArray.end(); ++i )
    {
        // Insert an item node.
        HTREEITEM hItem = pTree->InsertItem( CString( (*i)->getName().c_str() ), 0, 0, hRoot );
        // Set node data to MAXDWORD.
        pTree->SetItemData( hItem, MAXDWORD );
        // Query for elements
        QueryVCDPropertyElements( *i, hItem );
        // Make sure the tree is expanded.
        pTree->Expand( hItem, TVE_EXPAND );
    }
}

QueryVCDPropertyItems iterates through all available property items. For each item, it creates a new node. Every node that represents an item is a direct child of the root node. The node is labeled with the name of the IVCDPropertyItem (e.g. "Brightness", "Gain" or "Exposure"). Then, QueryVCDPropertyElements is called for the new node. QueryVCDPropertyElements looks as follows:

void CVCDPropertyTreeDlg::QueryVCDPropertyElements( tIVCDPropertyItemPtr pItem, HTREEITEM hItem )
{
    CTreeCtrl* pTree = (CTreeCtrl*)GetDlgItem( IDC_VCDTREE );
    tVCDPropertyElementArray elemArray = pItem->getElements();
    for( tVCDPropertyElementArray::const_iterator i = elemArray.begin(); i != elemArray.end(); ++i )
    {
        CString name;
        int imgIndex;

        GUID eid = (*i)->getElementID();
        if( eid == VCDElement_Value )
        {
            name = "VCDElement_Value";
            imgIndex = 1;
        }
        else if( eid == VCDElement_Auto )
        {
            name = "VCDElement_Auto";
            imgIndex = 2;
        }
        else if( eid == VCDElement_OnePush )
        {
            name = "VCDElement_OnePush";
            imgIndex = 5;
        }
        else if( eid == VCDElement_WhiteBalanceBlue )
        {
            name = "VCDElement_WhiteBalanceBlue";
            imgIndex = 1;
        }
        else if( eid == VCDElement_WhiteBalanceRed )
        {
            name = "VCDElement_WhiteBalanceRed";
            imgIndex = 1;
        }
        else
        {
            name = "Unknown element";
            imgIndex = 1;
        }
        name = name + ": '" + (*i)->getName().c_str() + "'";
        HTREEITEM hElem = pTree->InsertItem( name, imgIndex, imgIndex, hItem );
        // Set node data to MAXDWORD.
        pTree->SetItemData( hElem, MAXDWORD );
        QueryVCDPropertyInterfaces( *i, hElem );
        pTree->Expand( hElem, TVE_EXPAND );
    }
}

QueryVCDPropertyElements iterates through all available elements. For each element, it creates a new node. Each node that represents an element is a child of the node which represents the IVCDPropertyItem to which this IVCDPropertyElement belongs. The if..then..else block determines the appropriate graphical tree element for the node and sets its base name depending on the type of the element. The name of the IVCDPropertyElement (e.g. "Value", "Auto" or "Enable") is added to the base name and then used to label the node. Then, QueryVCDPropertyInterfaces is called for the new node. QueryVCDPropertyInterfaces looks as follows:

void CVCDPropertyTreeDlg::QueryVCDPropertyInterfaces( tIVCDPropertyElementPtr pElem, HTREEITEM hElem )
{
    CTreeCtrl* pTree = (CTreeCtrl*)GetDlgItem( IDC_VCDTREE );
    tVCDPropertyInterfaceArray itfArray = pElem->getInterfaces();
    for( tVCDPropertyInterfaceArray::const_iterator i = itfArray.begin(); i != itfArray.end(); ++i )
    {
        CString name;
        int imgIndex;
        GUID iid = (*i)->getInterfaceID();
        if( iid == IID_IVCDRangeProperty )
        {
            name = "Range";
            imgIndex = 3;
        }
        else if( iid == IID_IVCDSwitchProperty )
        {
            name = "Switch";
            imgIndex = 4;
        }
        else if( iid == IID_IVCDButtonProperty )
        {
            name = "Button";
            imgIndex = 5;
        }
        else if( iid == IID_IVCDMapStringsProperty )
        {
            name = "MapStrings";
            imgIndex = 6;
        }
        else if( iid == IID_IVCDAbsoluteValueProperty )
        {
            name = "AbsoluteValue";
            imgIndex = 3;
        }
        else
        {
            name = "Unknown Interface";
            imgIndex = 3;
        }
        // Insert the interface item
        HTREEITEM hItf = pTree->InsertItem( name, imgIndex, imgIndex, hElem );
        // Store the interface path within the vector.
        CInterfacePath ItfPath;
        ItfPath.itemID = (*i)->getParent()->getParent()->getItemID();
        ItfPath.elementID = (*i)->getParent()->getElementID();
        ItfPath.interfaceID = (*i)->getInterfaceID();
        m_InterfaceVector.push_back( ItfPath );
        // Store the position in the vector as node data
        pTree->SetItemData( hItf, m_InterfaceVector.size()-1 );
    }
}

QueryVCDPropertyInterfaces iterates through all available interfaces of an element. For each interface, it creates a new node. Each node that represents an interface is a child of the node which represents the IVCDPropertyElement to which this IVCDPropertyInterface belongs. The if..then..else block determines the appropriate graphical tree element for the node and sets its name depending on the type of the interface. The name is then used to label the node. Now that we have an interface node, the interface path is created. An interface path consists of an itemID, an elementID and an interfaceID. The interfaceID can be retrieved directly from the current interface. The elementID must be retrieved from the parent of the interface. The parent is retrieved with IVCDPropertyInterface::getParent. Finally, the itemID is retrieved from the parent of the parent of the interface. As mentioned above, we use the container class CInterfacePath to store the interface path in a vector. The index in the vector is stored in the node with SetItemData. The interface node makes up a leaf of the tree.

Accessing and Manipulating a VCDProperty

Now that the tree is build up, we, of course, want to access the properties. To do this, add an SelChanged event for the tree view and insert the following code:

void CVCDPropertyTreeDlg::OnSelchangedVcdtree(NMHDR* pNMHDR, LRESULT* pResult)
{
    NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;
    CTreeCtrl* pTree = (CTreeCtrl*)GetDlgItem( IDC_VCDTREE );
    DWORD index = pTree->GetItemData( pNMTreeView->itemNew.hItem );
    // If the index equals MAXDWORD, the node is not a leaf.
    if( index == MAXDWORD )
        return;
    // Destroy the buddy window
    m_pBuddy = NULL;
    // Retrieve the interface path
    CInterfacePath ItfPath = m_InterfaceVector.at( index );

    // Get a pointer to the interface specified by itemID, elementID and interfaceID.
    tIVCDPropertyInterfacePtr pItf = m_Grabber.getVCDPropertyInterface( ItfPath.itemID,
                                                                        ItfPath.elementID,
                                                                        ItfPath.interfaceID );
    // Build an appropriate text for the frame.
    CString frameText( pItf->getParent()->getParent()->getName().c_str() );
    // Test whether the interface is IVCDRangeProperty
    if( ItfPath.interfaceID == IID_IVCDRangeProperty )
    {
        // Acquire the interface pointer
        tIVCDRangePropertyPtr pRangeItf;
        pItf->QueryInterface( pRangeItf );
        if( pRangeItf == 0 )
            return;
        // Create a range slider control
        CVCDRangeSlider* pSlider = new CVCDRangeSlider();
        pSlider->Create( WS_CHILD|TBS_NOTICKS, DpiAdjustedRect( 300, 450, 500, 480 ), this, 0 );
        // Create a buddy window to display the value text
        CStatic* pBuddy = new CStatic();
        pBuddy->Create( TEXT("0"), WS_VISIBLE|WS_CHILD|SS_CENTERIMAGE, DpiAdjustedRect( 0, 0, 100, 23 ), this );
        pBuddy->SetFont( GetDlgItem( IDC_VCDTREE )->GetFont() );
        pSlider->SetBuddy( pBuddy, FALSE );
        m_pBuddy = pBuddy;
        pSlider->setupCtrl( pRangeItf );
        m_pCurrentControl = pSlider;
        // Add the element name to the frame text
        frameText += TEXT(": ");
        frameText += pItf->getParent()->getName().c_str();
    }
    // Test whether the interface is IVCDSwitchProperty
    else if( ItfPath.interfaceID == IID_IVCDSwitchProperty )
    {
        // Acquire the interface pointer
        tIVCDSwitchPropertyPtr pSwitchItf;
        pItf->QueryInterface( pSwitchItf );
        if( pSwitchItf == 0 )
            return;
        // Create a switch check control
        CVCDSwitchCheck* pButton = new CVCDSwitchCheck();
        pButton->Create( CString( pItf->getParent()->getName().c_str() ), WS_CHILD|BS_CHECKBOX, DpiAdjustedRect( 300, 450, 500, 480 ), this, 0 );
        pButton->SetFont( GetDlgItem( IDC_VCDTREE )->GetFont() );
        pButton->setupCtrl( pSwitchItf, 0 );
        m_pCurrentControl = pButton;
    }
    // Test whether the interface is IVCDButtonProperty
    else if( ItfPath.interfaceID == IID_IVCDButtonProperty )
    {
        // Acquire the interface pointer
        tIVCDButtonPropertyPtr pButtonItf;
        pItf->QueryInterface( pButtonItf );
        if( pButtonItf == 0 )
            return;
        // Create a button control
        CVCDButtonButton* pButton = new CVCDButtonButton();
        pButton->Create( CString( pItf->getParent()->getName().c_str() ), WS_CHILD|BS_PUSHBUTTON, DpiAdjustedRect( 300, 450, 380, 474 ), this, 0 );
        pButton->SetFont( GetDlgItem( IDC_VCDTREE )->GetFont() );
        pButton->setupCtrl( pButtonItf, 0 );
        m_pCurrentControl = pButton;
    }
    // Test whether the interface is IVCDMapStringsProperty
    else if( ItfPath.interfaceID == IID_IVCDMapStringsProperty )
    {
        // Acquire the interface pointer
        tIVCDMapStringsPropertyPtr pMapStringsItf;
        pItf->QueryInterface( pMapStringsItf );
        if( pMapStringsItf == 0 )
            return;
        // Create a combo control
        CVCDMapStringsCombo* pCombo = new CVCDMapStringsCombo();
        pCombo->Create( WS_CHILD|CBS_DROPDOWNLIST|WS_VSCROLL, DpiAdjustedRect( 300, 450, 500, 550 ), this, 0 );
        pCombo->SetFont( GetDlgItem( IDC_VCDTREE )->GetFont() );
        pCombo->setupCtrl( pMapStringsItf );
        m_pCurrentControl = pCombo;
        // Add the element name to the frame text
        frameText += TEXT(": ");
        frameText += pItf->getParent()->getName().c_str();
    }
    // Test whether the interface is IVCDAbsoluteValueProperty
    else if( ItfPath.interfaceID == IID_IVCDAbsoluteValueProperty )
    {
        // Acquire the interface pointer
        tIVCDAbsoluteValuePropertyPtr pAbsValItf;
        pItf->QueryInterface( pAbsValItf );
        if( pAbsValItf == 0 )
            return;
        // Create a absolute value slider control
        CVCDAbsValSlider* pSlider = new CVCDAbsValSlider();
        pSlider->Create( WS_CHILD|TBS_NOTICKS, DpiAdjustedRect( 300, 450, 500, 480 ), this, 0 );
        CStatic* pBuddy = new CStatic();
        pBuddy->Create( TEXT("0"), WS_VISIBLE|WS_CHILD|SS_CENTERIMAGE, DpiAdjustedRect( 0, 0, 100, 23 ), this );
        pBuddy->SetFont( GetDlgItem( IDC_VCDTREE )->GetFont() );
        pSlider->SetBuddy( pBuddy, FALSE );
        m_pBuddy = pBuddy;
        pSlider->setupCtrl( pAbsValItf );
        m_pCurrentControl = pSlider;
        // Add the element name to the frame text
        frameText += TEXT(": ");
        frameText += pItf->getParent()->getName().c_str();
    }
    // Show the control in case it was (initially) hidden
    if( m_pCurrentControl != 0 )
        m_pCurrentControl->ShowWindow( SW_SHOW );
    // Set the frame title
    GetDlgItem( IDC_CTRL_FRAME )->SetWindowText( frameText );
    *pResult = 0;
}

If you now click a node in the tree view, the method above is called. If a leaf is clicked, the interface path is retrieved from the vector and the appropriate interface is returned by the findInterface method. Next, the type of interface that we received is checked, so that the correct control can be initialized and displayed. This is done in the if..then..else block, where the appropriate control is dynamically created.

<< Programmer's Guide