SWIFTuser Studios

A small game development studio that works on sciency application too

MFC Multi-selection CTreeCtrl

January 15
by anegri 15. January 2014 08:38

Multi select enable tree control

 

Header for custom derived control


class CTreeCtrlX : public CTreeCtrl
{

protected:
    HTREEITEM    m_hItemFirstSel;        // Init to NULL in constructor
    DWORD        m_dwDragStart;
   
    void ClearSelection();
    BOOL SelectItems(HTREEITEM hItemFrom, HTREEITEM hItemTo);
    HTREEITEM GetFirstSelectedItem();
    HTREEITEM GetNextSelectedItem( HTREEITEM hItem );
    HTREEITEM GetPrevSelectedItem( HTREEITEM hItem );

public:
    DECLARE_MESSAGE_MAP()
    afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
    afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags);
};

 

Implementation Source


BEGIN_MESSAGE_MAP(CTreeCtrlX, CTreeCtrl)
    ON_WM_LBUTTONDOWN()
    ON_WM_KEYDOWN()
END_MESSAGE_MAP()

void CTreeCtrlX::OnLButtonDown(UINT nFlags, CPoint point)
{
    // Set focus to control if key strokes are needed.
    // Focus is not automatically given to control on lbuttondown
 
    m_dwDragStart = GetTickCount();
 
    if(nFlags & MK_CONTROL )
    {
        // Control key is down
        UINT flag;
        HTREEITEM hItem = HitTest( point, &flag );
        if( hItem )
        {
            // Toggle selection state
            UINT uNewSelState =
                GetItemState(hItem, TVIS_SELECTED) & TVIS_SELECTED ?
                            0 : TVIS_SELECTED;
           
            // Get old selected (focus) item and state
            HTREEITEM hItemOld = GetSelectedItem();
            UINT uOldSelState  = hItemOld ?
                    GetItemState(hItemOld, TVIS_SELECTED) : 0;
           
            // Select new item
            if( GetSelectedItem() == hItem )
                SelectItem( NULL );        // to prevent edit
            CTreeCtrl::OnLButtonDown(nFlags, point);
 
            // Set proper selection (highlight) state for new item
            SetItemState(hItem, uNewSelState,  TVIS_SELECTED);
 
            // Restore state of old selected item
            if (hItemOld && hItemOld != hItem)
                SetItemState(hItemOld, uOldSelState, TVIS_SELECTED);
 
            m_hItemFirstSel = NULL;
 
            return;
        }
    }
    else if(nFlags & MK_SHIFT)
    {
        // Shift key is down
        UINT flag;
        HTREEITEM hItem = HitTest( point, &flag );
 
        // Initialize the reference item if this is the first shift selection
        if( !m_hItemFirstSel )
            m_hItemFirstSel = GetSelectedItem();
 
        // Select new item
        if( GetSelectedItem() == hItem )
            SelectItem( NULL );            // to prevent edit
        CTreeCtrl::OnLButtonDown(nFlags, point);
 
        if( m_hItemFirstSel )
        {
            SelectItems( m_hItemFirstSel, hItem );
            return;
        }
    }
    else
    {
        // Normal - remove all selection and let default
        // handler do the rest
        ClearSelection();
        m_hItemFirstSel = NULL;
    }
 
    CTreeCtrl::OnLButtonDown(nFlags, point);
}

void CTreeCtrlX::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
    if ( (nChar==VK_UP || nChar==VK_DOWN) && GetKeyState( VK_SHIFT )&0x8000)
    {
        // Initialize the reference item if this is the first shift selection
        if( !m_hItemFirstSel )
        {
            m_hItemFirstSel = GetSelectedItem();
            ClearSelection();
        }
 
        // Find which item is currently selected
        HTREEITEM hItemPrevSel = GetSelectedItem();
 
        HTREEITEM hItemNext;
        if ( nChar==VK_UP )
            hItemNext = GetPrevVisibleItem( hItemPrevSel );
        else
            hItemNext = GetNextVisibleItem( hItemPrevSel );
 
        if ( hItemNext )
        {
            // Determine if we need to reselect previously selected item
            BOOL bReselect =
                !( GetItemState( hItemNext, TVIS_SELECTED ) & TVIS_SELECTED );
 
            // Select the next item - this will also deselect the previous item
            SelectItem( hItemNext );
 
            // Reselect the previously selected item
            if ( bReselect )
                SetItemState( hItemPrevSel, TVIS_SELECTED, TVIS_SELECTED );
        }
        return;
    }
    else if( nChar >= VK_SPACE )
    {
        m_hItemFirstSel = NULL;
        ClearSelection();
    }
    CTreeCtrl::OnKeyDown(nChar, nRepCnt, nFlags);
}

void CTreeCtrlX::ClearSelection()
{
    // This can be time consuming for very large trees
    // and is called every time the user does a normal selection
    // If performance is an issue, it may be better to maintain
    // a list of selected items
    for ( HTREEITEM hItem=GetRootItem(); hItem!=NULL; hItem=GetNextItem( hItem, TVGN_NEXT ) )
        if ( GetItemState( hItem, TVIS_SELECTED ) & TVIS_SELECTED )
            SetItemState( hItem, 0, TVIS_SELECTED );
}

// SelectItems    - Selects items from hItemFrom to hItemTo. Does not
//        - select child item if parent is collapsed. Removes
//        - selection from all other items
// hItemFrom    - item to start selecting from
// hItemTo    - item to end selection at.
BOOL CTreeCtrlX::SelectItems(HTREEITEM hItemFrom, HTREEITEM hItemTo)
{
    HTREEITEM hItem = GetRootItem();
 
    // Clear selection upto the first item
    while ( hItem && hItem!=hItemFrom && hItem!=hItemTo )
    {
        hItem = GetNextVisibleItem( hItem );
        SetItemState( hItem, 0, TVIS_SELECTED );
    }
 
    if ( !hItem )
        return FALSE;    // Item is not visible
 
    SelectItem( hItemTo );
 
    // Rearrange hItemFrom and hItemTo so that hItemFirst is at top
    if( hItem == hItemTo )
    {
        hItemTo = hItemFrom;
        hItemFrom = hItem;
    }
 
 
    // Go through remaining visible items
    BOOL bSelect = TRUE;
    while ( hItem )
    {
        // Select or remove selection depending on whether item
        // is still within the range.
        SetItemState( hItem, bSelect ? TVIS_SELECTED : 0, TVIS_SELECTED );
 
        // Do we need to start removing items from selection
        if( hItem == hItemTo )
            bSelect = FALSE;
 
        hItem = GetNextVisibleItem( hItem );
    }
 
    return TRUE;
}

HTREEITEM CTreeCtrlX::GetFirstSelectedItem()
{
    for ( HTREEITEM hItem = GetRootItem(); hItem!=NULL; hItem = GetNextItem( hItem, TVGN_NEXT ) )
        if ( GetItemState( hItem, TVIS_SELECTED ) & TVIS_SELECTED )
            return hItem;
 
    return NULL;
}
 
HTREEITEM CTreeCtrlX::GetNextSelectedItem( HTREEITEM hItem )
{
    for ( hItem = GetNextItem( hItem, TVGN_NEXT ); hItem!=NULL; hItem = GetNextItem( hItem, TVGN_NEXT ) )
        if ( GetItemState( hItem, TVIS_SELECTED ) & TVIS_SELECTED )
            return hItem;
 
    return NULL;
}
 
HTREEITEM CTreeCtrlX::GetPrevSelectedItem( HTREEITEM hItem )
{
    for ( hItem = GetNextItem( hItem, TVGN_PREVIOUS ); hItem!=NULL; hItem = GetNextItem( hItem, TVGN_PREVIOUS ) )
        if ( GetItemState( hItem, TVIS_SELECTED ) & TVIS_SELECTED )
            return hItem;
 
    return NULL;
}

 

based from this article http://www.codeguru.com/cpp/controls/treeview/misc-advanced/article.php/c723/Allowing-multiple-selection.htm