[Okta Webinar] Learn how to a build a cloud-first strategyRegister Now

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 1515
  • Last Modified:

Multiple selection in tree control using Win32 API calls (NOT MFC)

Hi,

I have created a tree view control using Win32 APi calls (NOT MFC) for my project, how can i do the multi-select items on the tree?

Thanks,

J
0
NoranEngineering
Asked:
NoranEngineering
  • 6
  • 5
1 Solution
 
itsmeandnobodyelseCommented:
The tree control is a single-selection control. As far as I remember it isn't impossible to make it a multiple-selection control by catching the notification messages and always turn the states and state images in an appropriate way. However, it is fighting against the built-in logic. Much easeier is it to either use a owner-drawn listbox rather than a tree control. You can set it to multiple selection and it is not very difficult to draw pictures, lines, and text in a similar way than doing it with a tree control. The other choice I know is to use state images rather than multiple-selection in a tree control. You may have two state images - checked and unchecked - that can be switched by sending the TVM_SETITEM message and a properly filled TVITEM struct.

Here two samples from MSDN:

BOOL TreeView_SetCheckState(HWND hwndTreeView, HTREEITEM hItem, BOOL fCheck)
{
    TVITEM tvItem;

    tvItem.mask = TVIF_HANDLE | TVIF_STATE;
    tvItem.hItem = hItem;
    tvItem.stateMask = TVIS_STATEIMAGEMASK;

    /*
    Since state images are one-based, 1 in this macro turns the check off, and
    2 turns it on.
    */
    tvItem.state = INDEXTOSTATEIMAGEMASK((fCheck ? 2 : 1));

    return TreeView_SetItem(hwndTreeView, &tvItem);  // send TVM_SETITEM message
}

The following example function illustrates how to retrieve an item's check state.

BOOL TreeView_GetCheckState(HWND hwndTreeView, HTREEITEM hItem)
{
    TVITEM tvItem;

    // Prepare to receive the desired information.
    tvItem.mask = TVIF_HANDLE | TVIF_STATE;
    tvItem.hItem = hItem;
    tvItem.stateMask = TVIS_STATEIMAGEMASK;

    // Request the information.
    TreeView_GetItem(hwndTreeView, &tvItem);  // send TVM_GETITEM message

    // Return zero if it's not checked, or nonzero otherwise.
    return ((BOOL)(tvItem.state >> 12) -1);
}

Regards, Alex
0
 
NoranEngineeringAuthor Commented:
Thanks,

I have done the following code, it sets multiple items to state CHECKED, but, it cant highlight (a gray box around the selected items) the items i have selected while it just highlight the first selected item... any ideas? Thanks so MUCH..

      BOOL bShift = (GetKeyState(VK_SHIFT) & 0x8000);
      
      int         iObjectID = -1;
      CNETreeItem *pTreeItem = NULL;      

      BOOL bDir = FALSE;
      HTREEITEM hSel = NULL;
      
      HTREEITEM   htItem = TreeView_GetDropHilight (m_ctlSysTreeView32.m_hWnd);
      
      if ( wParam == VK_DOWN )
      {
            if (bShift)
            {
                  hSel = TreeView_GetSelection(m_ctlSysTreeView32);
                  if (!m_hSelect)
                        m_hSelect = hSel;
                  HTREEITEM hNext = bDir ? TreeView_GetPrevVisible(m_ctlSysTreeView32.m_hWnd, hSel) :  
                                                                              TreeView_GetNextVisible(m_ctlSysTreeView32.m_hWnd, hSel);
                  if (!hNext)
                        hNext = hSel;
                  SelectRange(m_hSelect, hNext, TRUE);                  

            }
               }

void SelectRange(HTREEITEM hFirst, HTREEITEM hLast, BOOL bOnly /*= TRUE*/)
{
      
      int         iObjectID = -1;
      CNETreeItem *pItem = NULL;      
      
      HTREEITEM hItem = TreeView_GetRoot (m_ctlSysTreeView32.m_hWnd);
      

      while (hItem)
      {
            if ((hItem == hFirst) || (hItem == hLast))
            {
                  if (hFirst != hLast)
                  {
                        if (!TreeView_GetCheckState (m_ctlSysTreeView32.m_hWnd, hItem))
                              TreeView_SetCheckState (m_ctlSysTreeView32.m_hWnd, hItem, CHECKED);
                        hItem = TreeView_GetNextVisible(m_ctlSysTreeView32.m_hWnd, hItem);
                  }
                  break;
            }
            
            if (bOnly && TreeView_GetCheckState (m_ctlSysTreeView32.m_hWnd, hItem))
                  TreeView_SetCheckState (m_ctlSysTreeView32.m_hWnd, hItem, UNCHECKED);
            
            hItem = TreeView_GetNextVisible(m_ctlSysTreeView32.m_hWnd, hItem);
      }
      
      //select rest of range
      while (hItem)
      {
            if (!TreeView_GetCheckState (m_ctlSysTreeView32.m_hWnd, hItem))
                  TreeView_SetCheckState (m_ctlSysTreeView32.m_hWnd, hItem, CHECKED);
            
            if ((hItem == hFirst) || (hItem == hLast))
            {
                  hItem = TreeView_GetNextVisible(m_ctlSysTreeView32.m_hWnd, hItem);
                  break;
            }
            
            hItem = TreeView_GetNextVisible(m_ctlSysTreeView32.m_hWnd, hItem);
      }
      if (!bOnly)
            return;
            
}
0
Concerto Cloud for Software Providers & ISVs

Can Concerto Cloud Services help you focus on evolving your application offerings, while delivering the best cloud experience to your customers? From DevOps to revenue models and customer support, the answer is yes!

Learn how Concerto can help you.

 
itsmeandnobodyelseCommented:
>>>> while it just highlight the first selected item

Yes, the problem is that high-liting is done automatically when sending the TVM_SELECTITEM message. Instead of doing that you might try to set the TVIS_SELECTED state bit yourself for all selected items. Try to catch the TVN_SELCHANGING notification to know which items need to get hilited. But return FALSE to that notification in order to not invoke any automatism. Change the hilite state by an explicit call to SetItem.

Regards, Alex

BTW, I would have honored to get the feedback *prior* to you giving a 'B' grade. That would have opened me the chance to go for a 'A'.

0
 
NoranEngineeringAuthor Commented:
Hi Alex,

I tried to set TVIS_SELECTED state by myself:

                  pItem->m_pTvItem.state = TVIS_SELECTED;                  
                  TreeView_SetItem (m_ctlSysTreeView32.m_hWnd, &(pItem->m_pTvItem));

but still got the same problem.. just the first item got highlighted.

//return FALSE to that notification in order to not invoke any automatism ==> i have returned false in the the TVN_SELCHANGING notification, can you explain how to do that?

Thanks,

PS: i want to change grade to 'A'. but how can i do that?
0
 
itsmeandnobodyelseCommented:
>>>> but still got the same problem.. just the first item got highlighted

I got more than one hilited item by catching the TVN_SELCHANGING notification. I used MFC and message maps, so my handler function needs to be converted to native SDK:

void CTestfrmView::OnSelchangingTree1(NMHDR* pNMHDR, LRESULT* pResult)
{
   NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;
   // TODO: Add your control notification handler code here
      
    pNMTreeView->itemOld.mask  = TVIF_STATE;
    pNMTreeView->itemOld.state = TVIS_SELECTED;
    m_tree.SetItem(&pNMTreeView->itemOld);

    pNMTreeView->itemNew.mask  = TVIF_STATE;
    pNMTreeView->itemNew.state = TVIS_SELECTED;
    m_tree.SetItem(&pNMTreeView->itemNew);

    *pResult = 1;
}

>>>> void CTestfrmView::OnSelchangingTree1(NMHDR* pNMHDR, LRESULT* pResult)

I assume you'll get the TVN_SELCHANGING notification by catching WM_NOTIFY message and wParam == TVN_SELCHANGING. Then lparam is a pointer to NMTREEVIEW where you got the itemOld and and itemNew members as in my code above.

>>>> i have returned false in the the TVN_SELCHANGING notification

Actually, the statement was wrong as I returned 1 == true in the pResult argument. That means I did not pass the notification to default handling but but told the calling function that the message was handled. In your case it means you should not call default windows proc after you handled the notification.

>>>> PS: i want to change grade to 'A'. but how can i do that?

You may send a request to Community Support (CS) which you can do via the Support menu. But maybe you should wait until the 'A' is appropriate.

Regards, Alex
0
 
NoranEngineeringAuthor Commented:
I have returned 1 in the TVN_SELCHANGING notification, and now, no highlight box appears, just the focus box shows up on the first item i selected....
THANKS>>>>
0
 
itsmeandnobodyelseCommented:
>>>> I have returned 1 in the TVN_SELCHANGING notification, and now, no highlight box appears

Please post the code where you are handling the TVN_SELCHANGING.

Regards, Alex
0
 
NoranEngineeringAuthor Commented:
This is where i have the TVN_SELCHANGING:

long CTreeClass::OnNotify(HWND hWndDlg, LPNMHDR pNMHDR)
{
   if (pNMHDR)
   {
 
       if (pNMHDR->code == TVN_SELCHANGING)
      {
              LRESULT* pResult = FALSE;      
              BOOL bShift = (GetKeyState(VK_SHIFT) & 0x8000);      
             
              if (bShift) // if shift is pressed down => multiple selection => force the old item cant change the state
              {              
                                NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;
      
                               pNMTreeView->itemOld.mask  = TVIF_STATE;
                                         pNMTreeView->itemOld.state = TVIS_SELECTED;
                               TreeView_SetItem (m_ctlSysTreeView32.m_hWnd, &(pNMTreeView->itemOld));

                                         pNMTreeView->itemNew.mask  = TVIF_STATE;
                                         pNMTreeView->itemNew.state = TVIS_SELECTED;
                                  TreeView_SetItem (m_ctlSysTreeView32.m_hWnd, &(pNMTreeView->itemNew));
                  
                                           return (1);
                                    }
      
      }
   }

   return(0);
}

Thanks,..
0
 
itsmeandnobodyelseCommented:
>>>> BOOL bShift = (GetKeyState(VK_SHIFT) & 0x8000);

Actually, it is not guaranteed that the notification was sent while the SHIFT key was pressed ... There might be further notifications after the key was released ...

Try to set the selection always, just to see whether multiple selection works or not. If yes, you should hold the selection status of each item seperatedly in an array or tree (you might use the lparam member of each item) and write a function that sets the status of any (visible) tree member. Then, you might call that function at any notification that might have changed selection and always return (1) to indicate that you have handled the message.

Note, when calling CTreeClass::OnNotify(HWND hWndDlg, LPNMHDR pNMHDR) you always need to check the return value and do not further process the message if OnNotify returned 1.

Regards, Alex

0
 
NoranEngineeringAuthor Commented:
I have commented out the if(bShift) amd change to if(1), and now it doesnt allow me to select anything, even though i click on the item, the item just got hightlighted in liek 1 second, and it turns back to unselected....
0
 
itsmeandnobodyelseCommented:
>>>>  the item just got hightlighted in liek 1 second, and it turns back to unselected....

That is what I meant with "fighting against the defaults"  ;-)

You need a different handler function for any type of notification. The code above only applies to TVN_SELCHANGING but *not* to other notifications, e. g. TVN_SELCHANGED. If you omit all notifications with (1) == handled, the behavior *must* be strange. As I told before, I wouldn't care about the state info returned with TV_ITEM struct but have an own status management. You could have a class for any item type. All classes were derived from one baseclass, say class TreeItem. In the baseclass you have a status member (bool) that tells whether the item was selected or not. You may store the baseclass pointer of the derived object for any item in the lparam member of TV_ITEM (don't forget to switch on the LVIF_PARAM bit). So, any GetItem or any notification would return the pointer to the object and you easily can decide whether it should get hilited or not.

Regards, Alex
0

Featured Post

Keep up with what's happening at Experts Exchange!

Sign up to receive Decoded, a new monthly digest with product updates, feature release info, continuing education opportunities, and more.

  • 6
  • 5
Tackle projects and never again get stuck behind a technical roadblock.
Join Now