首    页 界面/窗口 网络/通讯 数据库 组件开发 图像/多媒体 NET/Web 其它技术 源码下载 资料下载 软件共享 软件外包 曲艺杂谈
栏目导航:  首    页  |  界面/窗口  |  树型控件   


一个支持多选的树类


原作者:不详    源出处:CSDN    发布者:施昌权    发布类型:转载    发布日期:2008-10-13

              

//头文件

///////////////////////////////////////////////////////////////////////////////
//
// CTreeCtrlEx - Multiple selection tree control for MFC
//
// Bendik Engebretsen (c) 1997-1999
// bendik@techsoft.no
// http://www.techsoft.no/bendik/
//
//

#ifndef __TREECTRLEX_H
#define __TREECTRLEX_H

#define TVGN_EX_ALL   0x000F

/////////////////////////////////////////////////////////////////////////////
// CTreeCtrlEx window

class CTreeCtrlEx : public CTreeCtrl
{
  DECLARE_DYNAMIC(CTreeCtrlEx)

// Construction
public:
  CTreeCtrlEx() : m_bSelectPending(FALSE), m_hClickedItem(NULL), m_hFirstSelectedItem(NULL), m_bSelectionComplete(TRUE), m_bEditLabelPending(FALSE) {}
  BOOL Create(DWORD dwStyle, DWORD dwExStyle, const RECT& rect, CWnd* pParentWnd, UINT nID);
  BOOL Create(DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID);

// Attributes
public:
  UINT GetSelectedCount() const;
  HTREEITEM GetNextItem(HTREEITEM hItem, UINT nCode);
  HTREEITEM GetFirstSelectedItem();
  HTREEITEM GetNextSelectedItem(HTREEITEM hItem);
  HTREEITEM GetPrevSelectedItem(HTREEITEM hItem);
  HTREEITEM ItemFromData(DWORD dwData, HTREEITEM hStartAtItem=NULL) const;

 BOOL SelectItemEx(HTREEITEM hItem, BOOL bSelect=TRUE);

 BOOL SelectItems(HTREEITEM hFromItem, HTREEITEM hToItem);
  void ClearSelection(BOOL bMultiOnly=FALSE);

protected:
  void SelectMultiple( HTREEITEM hClickedItem, UINT nFlags, CPoint point );

private:
  BOOL  m_bSelectPending;
  CPoint  m_ptClick;
  HTREEITEM m_hClickedItem;
  HTREEITEM m_hFirstSelectedItem;
  BOOL  m_bSelectionComplete;
  BOOL  m_bEditLabelPending;
  UINT  m_idTimer;

// Operations
public:

// Overrides
  // ClassWizard generated virtual function overrides
  //{{AFX_VIRTUAL(CTreeCtrlEx)
  //}}AFX_VIRTUAL

// Implementation
public:
  virtual ~CTreeCtrlEx() {}

 // Generated message map functions
protected:
  //{{AFX_MSG(CTreeCtrlEx)
  afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
  afx_msg void OnLButtonUp(UINT nFlags, CPoint point);
  afx_msg void OnMouseMove(UINT nFlags, CPoint point);
  afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags);
  afx_msg BOOL OnItemexpanding(NMHDR* pNMHDR, LRESULT* pResult);
  afx_msg BOOL OnSetfocus(NMHDR* pNMHDR, LRESULT* pResult);
  afx_msg BOOL OnKillfocus(NMHDR* pNMHDR, LRESULT* pResult);
  afx_msg void OnRButtonDown(UINT nFlags, CPoint point);
  afx_msg BOOL OnSelchanged(NMHDR* pNMHDR, LRESULT* pResult);
  afx_msg void OnLButtonDblClk(UINT nFlags, CPoint point);
  afx_msg void OnTimer(UINT nIDEvent);
  //}}AFX_MSG

 DECLARE_MESSAGE_MAP()
};


HTREEITEM GetTreeItemFromData(CTreeCtrl& treeCtrl, DWORD dwData, HTREEITEM hStartAtItem=NULL);

#endif
 

 

//实现文件

///////////////////////////////////////////////////////////////////////////////
//
// CTreeCtrlEx - Multiple selection tree control for MFC
//
// Copyright ?1997-2003 Bendik Engebretsen
// bendik@techsoft.no
// http://www.techsoft.no/bendik/
//
// Oct 9,  1997 : Fixed problem with notification to parent (TVN_BEGINDRAG)
// Oct 17, 1997 : Fixed bug with deselection when collapsing node with no sibling
// Nov 5,  1997 : Fixed problem with label editing
// Feb 17, 1998 : Fixed another notfication to parent (TVN_KEYDOWN)
// Apr 27, 1998 : Added TVN_SELCHANGED notification to parent in SelectMultiple()
// Dec 21, 1998 : Fixed incorrect use of LVHT_... constants. Now using TVHT_...
// Mar 15, 1999 : Fixed problems when there is no selected item.
//                Fixed new problem with label editing
// Apr 16, 1999 : Fixed problem with double TVN_SELCHANGED notifications
//      Many thanx to Roel Schroeven for providing the solution!!
// Jul 2, 1999 : Added TVN_SELCHANGING notification when selecting/deselecting
//      selected item
// Oct 11, 1999 : Fixed quirks with Shift+Arrow Key selection. Thanx to Yariv Elani !
//      Also changed behaviour of Ctrl+Mouse click selection: The item is
//      unselected, but NOT 'focused' (i.e. same behaviour as a listctrl).
// Nov 16, 1999 : Fixed another quirk with Ctrl+Mouse click selection. Thanks to
//      Yariv Ben-Tovim!
// May 21, 2001 : Now supports Shift PgUp and PgDn selection. Thanks to Sergei
//      Antonov. Also fixed the too early TVN_SELCHANGED when key-selecting.
// May 31, 2001 : Fixed bug with uninitialized m_ptClick in OnLButtonDown. Thanks to
//      Cristian Rodriguez!
// Jun 20, 2001 : Fixed quirk with label editing/doubleclick. Once more, thanks
//      to Cristian Rodriguez. Also added treectrl wndclass registration.
// Jan 24, 2003 : Fixed bug when treeview has the TVS_DISABLEDRAGDROP style. Thanks to
//      Fernanda Diniz Tavarez
// Mar 20, 2003 : Fixed 'blinking' problem with TVS_SINGLEEXPAND style. Thanks to
//      H.-Joachim Riedel.
// Jun 10, 2003 : Migrated to Visual C++.NET / MFC 7.X

#include "stdafx.h"
#include "TreeCtrlEx.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

#define TCEX_EDITLABEL 1  // Edit label timer event

/////////////////////////////////////////////////////////////////////////////
// CTreeCtrlEx

BEGIN_MESSAGE_MAP(CTreeCtrlEx, CTreeCtrl)
  //{{AFX_MSG_MAP(CTreeCtrlEx)
  ON_WM_LBUTTONDOWN()
  ON_WM_LBUTTONUP()
  ON_WM_MOUSEMOVE()
  ON_WM_KEYDOWN()
  ON_NOTIFY_REFLECT_EX(TVN_ITEMEXPANDING, OnItemexpanding)
  ON_NOTIFY_REFLECT_EX(NM_SETFOCUS, OnSetfocus)
  ON_NOTIFY_REFLECT_EX(NM_KILLFOCUS, OnKillfocus)
  ON_NOTIFY_REFLECT_EX(TVN_SELCHANGED, OnSelchanged)
  ON_WM_RBUTTONDOWN()
  ON_WM_LBUTTONDBLCLK()
  ON_WM_TIMER() 
  //}}AFX_MSG_MAP
END_MESSAGE_MAP()

IMPLEMENT_DYNAMIC(CTreeCtrlEx, CTreeCtrl)

BOOL CTreeCtrlEx::Create(DWORD dwStyle, DWORD dwExStyle, const RECT& rect, CWnd* pParentWnd, UINT nID)
{
#if _MFC_VER < 0x0700
  return CreateEx( dwExStyle, WC_TREEVIEW, NULL, dwStyle,
   rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top,
   pParentWnd->GetSafeHwnd(), (HMENU)nID );
#else
  return CTreeCtrl::CreateEx( dwExStyle, dwStyle, rect, pParentWnd, nID );
#endif
}

BOOL CTreeCtrlEx::Create(DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID)
{
  return CTreeCtrl::Create(dwStyle, rect, pParentWnd, nID);
}


/////////////////////////////////////////////////////////////////////////////
// CTreeCtrlEx message handlers


///////////////////////////////////////////////////////////////////////////////
// The tree control dosn't support multiple selection. However we can simulate
// it by taking control of the left mouse click and arrow key press before the
// control gets them, and setting/clearing the TVIS_SELECTED style on the items

void CTreeCtrlEx::OnLButtonDown( UINT nFlags, CPoint point )
{

 UINT nHitFlags = 0;
  HTREEITEM hClickedItem = HitTest( point, &nHitFlags );

 // Must invoke label editing explicitly. The base class OnLButtonDown would normally
  // do this, but we can't call it here because of the multiple selection...
  if( !( nFlags&( MK_CONTROL|MK_SHIFT ) ) && ( GetStyle() & TVS_EDITLABELS ) && ( nHitFlags & TVHT_ONITEMLABEL ) )
   if ( hClickedItem == GetSelectedItem() )
   {
    // Clear multple selection before label editing
    ClearSelection();
    SelectItem( hClickedItem );

   // Invoke label editing
    m_bEditLabelPending = TRUE;
    m_idTimer = SetTimer(TCEX_EDITLABEL, GetDoubleClickTime(), NULL);

   return;
   }

 m_bEditLabelPending = FALSE;

 if( nHitFlags & TVHT_ONITEM )
  {
   SetFocus();

  m_hClickedItem = hClickedItem;

  // Is the clicked item already selected ?
   BOOL bIsClickedItemSelected = GetItemState( hClickedItem, TVIS_SELECTED ) & TVIS_SELECTED;

  if ( bIsClickedItemSelected )
   {
    // Maybe user wants to drag/drop multiple items!
    // So, wait until OnLButtonUp() to do the selection stuff.
    m_bSelectPending=TRUE;
   }
   else
   {
    SelectMultiple( hClickedItem, nFlags, point );
    m_bSelectPending=FALSE;
   }

  m_ptClick=point;
  }
  else
   CTreeCtrl::OnLButtonDown( nFlags, point );
}

void CTreeCtrlEx::OnLButtonUp( UINT nFlags, CPoint point )
{
  if ( m_bSelectPending )
  {
   // A select has been waiting to be performed here
   SelectMultiple( m_hClickedItem, nFlags, point );
   m_bSelectPending=FALSE;
  }

 m_hClickedItem=NULL;

 CTreeCtrl::OnLButtonUp( nFlags, point );
}


void CTreeCtrlEx::OnMouseMove( UINT nFlags, CPoint point )
{
  // If there is a select pending, check if cursor has moved so much away from the
  // down-click point that we should cancel the pending select and initiate
  // a drag/drop operation instead!

 if ( m_hClickedItem )
  {
   CSize sizeMoved = m_ptClick-point;

  if ( abs(sizeMoved.cx) > GetSystemMetrics( SM_CXDRAG ) || abs(sizeMoved.cy) > GetSystemMetrics( SM_CYDRAG ) )
   {
    m_bSelectPending=FALSE;

   // Notify parent that he may begin drag operation
    // Since we have taken over OnLButtonDown(), the default handler doesn't
    // do the normal work when clicking an item, so we must provide our own
    // TVN_BEGINDRAG notification for the parent!

   CWnd* pWnd = GetParent();
    if ( pWnd && !( GetStyle() & TVS_DISABLEDRAGDROP ) )
    {
     NM_TREEVIEW tv;

    tv.hdr.hwndFrom = GetSafeHwnd();
     tv.hdr.idFrom = GetWindowLong( GetSafeHwnd(), GWL_ID );
     tv.hdr.code = TVN_BEGINDRAG;

    tv.itemNew.hItem = m_hClickedItem;
     tv.itemNew.state = GetItemState( m_hClickedItem, 0xffffffff );
     tv.itemNew.lParam = GetItemData( m_hClickedItem );

    tv.ptDrag.x = point.x;
     tv.ptDrag.y = point.y;

    pWnd->SendMessage( WM_NOTIFY, tv.hdr.idFrom, (LPARAM)&tv );
    }

   m_hClickedItem=NULL;
   }
  }

 CTreeCtrl::OnMouseMove( nFlags, point );
}


void CTreeCtrlEx::SelectMultiple( HTREEITEM hClickedItem, UINT nFlags, CPoint point )
{
  // Start preparing an NM_TREEVIEW struct to send a notification after selection is done
  NM_TREEVIEW tv;
  memset(&tv.itemOld, 0, sizeof(tv.itemOld));

 CWnd* pWnd = GetParent();

 HTREEITEM hOldItem = GetSelectedItem();

 if ( hOldItem )
  {
   tv.itemOld.hItem = hOldItem;
   tv.itemOld.state = GetItemState( hOldItem, 0xffffffff );
   tv.itemOld.lParam = GetItemData( hOldItem );
   tv.itemOld.mask = TVIF_HANDLE|TVIF_STATE|TVIF_PARAM;
  }

 // Flag signaling that selection process is NOT complete.
  // (Will prohibit TVN_SELCHANGED from being sent to parent)
  m_bSelectionComplete = FALSE;

 // Action depends on whether the user holds down the Shift or Ctrl key
  if ( nFlags & MK_SHIFT )
  {
   // Select from first selected item to the clicked item
   if ( !m_hFirstSelectedItem )
    m_hFirstSelectedItem = GetSelectedItem();

  SelectItems( m_hFirstSelectedItem, hClickedItem );
  }
  else if ( nFlags & MK_CONTROL )
  {
   // Find which item is currently selected
   HTREEITEM hSelectedItem = GetSelectedItem();

  // Is the clicked item already selected ?
   BOOL bIsClickedItemSelected = GetItemState( hClickedItem, TVIS_SELECTED ) & TVIS_SELECTED;
   BOOL bIsSelectedItemSelected = FALSE;
   if ( hSelectedItem )
    bIsSelectedItemSelected = GetItemState( hSelectedItem, TVIS_SELECTED ) & TVIS_SELECTED;

  // Must synthesize a TVN_SELCHANGING notification
   if ( pWnd )
   {
    tv.hdr.hwndFrom = GetSafeHwnd();
    tv.hdr.idFrom = GetWindowLong( GetSafeHwnd(), GWL_ID );
    tv.hdr.code = TVN_SELCHANGING;

   tv.itemNew.hItem = hClickedItem;
    tv.itemNew.state = GetItemState( hClickedItem, 0xffffffff );
    tv.itemNew.lParam = GetItemData( hClickedItem );

   tv.itemOld.hItem = NULL;
    tv.itemOld.mask = 0;

   tv.action = TVC_BYMOUSE;

   tv.ptDrag.x = point.x;
    tv.ptDrag.y = point.y;

   pWnd->SendMessage( WM_NOTIFY, tv.hdr.idFrom, (LPARAM)&tv );
   }

  // If the previously selected item was selected, re-select it
   if ( bIsSelectedItemSelected )
    SetItemState( hSelectedItem, TVIS_SELECTED, TVIS_SELECTED );

  // We want the newly selected item to toggle its selected state,
   // so unselect now if it was already selected before
   if ( bIsClickedItemSelected )
    SetItemState( hClickedItem, 0, TVIS_SELECTED );
   else
   {
    SelectItem(hClickedItem);
    SetItemState( hClickedItem, TVIS_SELECTED, TVIS_SELECTED );
   }

  // If the previously selected item was selected, re-select it
   if ( bIsSelectedItemSelected && hSelectedItem != hClickedItem )
    SetItemState( hSelectedItem, TVIS_SELECTED, TVIS_SELECTED );

  // Store as first selected item (if not already stored)
   if ( m_hFirstSelectedItem==NULL )
    m_hFirstSelectedItem = hClickedItem;
  }
  else
  {
   // Clear selection of all "multiple selected" items first
   ClearSelection();

  // Then select the clicked item
   SelectItem( hClickedItem );
   SetItemState( hClickedItem, TVIS_SELECTED, TVIS_SELECTED );

  // Store as first selected item
   m_hFirstSelectedItem = hClickedItem;
  }

 // Selection process is now complete. Since we have 'eaten' the TVN_SELCHANGED
  // notification provided by Windows' treectrl, we must now produce one ourselves,
  // so that our parent gets to know about the change of selection.
  m_bSelectionComplete = TRUE;

 if ( pWnd )
  {
   tv.hdr.hwndFrom = GetSafeHwnd();
   tv.hdr.idFrom = GetWindowLong( GetSafeHwnd(), GWL_ID );
   tv.hdr.code = TVN_SELCHANGED;

  tv.itemNew.hItem = m_hClickedItem;
   tv.itemNew.state = GetItemState( m_hClickedItem, 0xffffffff );
   tv.itemNew.lParam = GetItemData( m_hClickedItem );
   tv.itemNew.mask = TVIF_HANDLE|TVIF_STATE|TVIF_PARAM;

  tv.action = TVC_UNKNOWN;

  pWnd->SendMessage( WM_NOTIFY, tv.hdr.idFrom, (LPARAM)&tv );
  }
}

void CTreeCtrlEx::OnKeyDown( UINT nChar, UINT nRepCnt, UINT nFlags )
{
  CWnd* pWnd = GetParent();

 if ( nChar==VK_NEXT || nChar==VK_PRIOR )
  {
   if ( !( GetKeyState( VK_SHIFT )&0x8000 ) )
   {
    // User pressed Pg key without holding 'Shift':
    // Clear multiple selection (if multiple) and let base class do
    // normal selection work!
    if ( GetSelectedCount()>1 )
     ClearSelection( TRUE );

   CTreeCtrl::OnKeyDown( nChar, nRepCnt, nFlags );
    m_hFirstSelectedItem = GetSelectedItem();
    return;
   }

  // Flag signaling that selection process is NOT complete.
   // (Will prohibit TVN_SELCHANGED from being sent to parent)
   m_bSelectionComplete = FALSE;

  // Let base class select the item
   CTreeCtrl::OnKeyDown( nChar, nRepCnt, nFlags );
   HTREEITEM hSelectedItem = GetSelectedItem();

  // Then select items in between
   SelectItems( m_hFirstSelectedItem, hSelectedItem );

  // Selection process is now complete. Since we have 'eaten' the TVN_SELCHANGED
   // notification provided by Windows' treectrl, we must now produce one ourselves,
   // so that our parent gets to know about the change of selection.
   m_bSelectionComplete = TRUE;

  if (pWnd)
   {
    NM_TREEVIEW tv;
    memset(&tv.itemOld, 0, sizeof(tv.itemOld));

   tv.hdr.hwndFrom = GetSafeHwnd();
    tv.hdr.idFrom = GetWindowLong(GetSafeHwnd(), GWL_ID);
    tv.hdr.code = TVN_SELCHANGED;

   tv.itemNew.hItem = hSelectedItem;
    tv.itemNew.state = GetItemState(hSelectedItem, 0xffffffff);
    tv.itemNew.lParam = GetItemData(hSelectedItem);
    tv.itemNew.mask = TVIF_HANDLE|TVIF_STATE|TVIF_PARAM;

   tv.action = TVC_UNKNOWN;

   pWnd->SendMessage(WM_NOTIFY, tv.hdr.idFrom, (LPARAM)&tv);
   }
  }
  else if ( nChar==VK_UP || nChar==VK_DOWN )
  {
   // Find which item is currently selected
   HTREEITEM hSelectedItem = GetSelectedItem();

  HTREEITEM hNextItem;
   if ( nChar==VK_UP )
    hNextItem = GetPrevVisibleItem( hSelectedItem );
   else
    hNextItem = GetNextVisibleItem( hSelectedItem );

  if ( !( GetKeyState( VK_SHIFT )&0x8000 ) )
   {
    // User pressed arrow key without holding 'Shift':
    // Clear multiple selection (if multiple) and let base class do
    // normal selection work!
    if ( GetSelectedCount()>1 )
     ClearSelection( TRUE );

   if ( hNextItem )
     CTreeCtrl::OnKeyDown( nChar, nRepCnt, nFlags );
    m_hFirstSelectedItem = GetSelectedItem();
    return;
   }

  if ( hNextItem )
   {
    // Flag signaling that selection process is NOT complete.
    // (Will prohibit TVN_SELCHANGED from being sent to parent)
    m_bSelectionComplete = FALSE;

   // If the next item is already selected, we assume user is
    // "moving back" in the selection, and thus we should clear
    // selection on the previous one
    BOOL bSelect = !( GetItemState( hNextItem, TVIS_SELECTED ) & TVIS_SELECTED );

   // Select the next item (this will also deselect the previous one!)
    SelectItem( hNextItem );

   // Now, re-select the previously selected item
    if ( bSelect || ( !( GetItemState( hSelectedItem, TVIS_SELECTED ) & TVIS_SELECTED ) ) )
     SelectItems( m_hFirstSelectedItem, hNextItem );

   // Selection process is now complete. Since we have 'eaten' the TVN_SELCHANGED
    // notification provided by Windows' treectrl, we must now produce one ourselves,
    // so that our parent gets to know about the change of selection.
    m_bSelectionComplete = TRUE;

   if (pWnd)
    {
     NM_TREEVIEW tv;
     memset(&tv.itemOld, 0, sizeof(tv.itemOld));

    tv.hdr.hwndFrom = GetSafeHwnd();
     tv.hdr.idFrom = GetWindowLong(GetSafeHwnd(), GWL_ID);
     tv.hdr.code = TVN_SELCHANGED;

    tv.itemNew.hItem = hNextItem;
     tv.itemNew.state = GetItemState(hNextItem, 0xffffffff);
     tv.itemNew.lParam = GetItemData(hNextItem);
     tv.itemNew.mask = TVIF_HANDLE|TVIF_STATE|TVIF_PARAM;

    tv.action = TVC_UNKNOWN;

    pWnd->SendMessage(WM_NOTIFY, tv.hdr.idFrom, (LPARAM)&tv);
    }
   }

  // Since the base class' OnKeyDown() isn't called in this case,
   // we must provide our own TVN_KEYDOWN notification to the parent

  CWnd* pWnd = GetParent();
   if ( pWnd )
   {
    NMTVKEYDOWN tvk;

   tvk.hdr.hwndFrom = GetSafeHwnd();
    tvk.hdr.idFrom = GetWindowLong( GetSafeHwnd(), GWL_ID );
    tvk.hdr.code = TVN_KEYDOWN;

   tvk.wVKey = nChar;
    tvk.flags = 0;

   pWnd->SendMessage( WM_NOTIFY, tvk.hdr.idFrom, (LPARAM)&tvk );
   }
  }
  else
   // Behave normally
   CTreeCtrl::OnKeyDown( nChar, nRepCnt, nFlags );
}


///////////////////////////////////////////////////////////////////////////////
// I want clicking on an item with the right mouse button to select the item,
// but not if there is currently a multiple selection

void CTreeCtrlEx::OnRButtonDown( UINT nFlags, CPoint point )
{
  UINT nHitFlags = 0;
  HTREEITEM hClickedItem = HitTest( point, &nHitFlags );

 if( nHitFlags&TVHT_ONITEM )
   if ( GetSelectedCount()<2 )
    SelectItem( hClickedItem );

 CTreeCtrl::OnRButtonDown( nFlags, point );
}


///////////////////////////////////////////////////////////////////////////////
// Get number of selected items

UINT CTreeCtrlEx::GetSelectedCount() const
{
  // Only visible items should be selected!
  UINT uCount=0;
  for ( HTREEITEM hItem = GetRootItem(); hItem!=NULL; hItem = GetNextVisibleItem( hItem ) )
   if ( GetItemState( hItem, TVIS_SELECTED ) & TVIS_SELECTED )
    uCount++;

 return uCount;
}


///////////////////////////////////////////////////////////////////////////////
// Overloaded to catch our own special code

HTREEITEM CTreeCtrlEx::GetNextItem(HTREEITEM hItem, UINT nCode)
{
  if (nCode==TVGN_EX_ALL)
  {
   // This special code lets us iterate through ALL tree items regardless
   // of their parent/child relationship (very handy)
   HTREEITEM hNextItem;

  // If it has a child node, this will be the next item
   hNextItem = GetChildItem( hItem );
   if (hNextItem)
    return hNextItem;

  // Otherwise, see if it has a next sibling item
   hNextItem = GetNextSiblingItem(hItem);
   if (hNextItem)
    return hNextItem;

  // Finally, look for next sibling to the parent item
   HTREEITEM hParentItem=hItem;
   while (!hNextItem && hParentItem)
   {
    // No more children: Get next sibling to parent
    hParentItem = GetParentItem(hParentItem);
    hNextItem = GetNextSiblingItem(hParentItem);
   }

  return hNextItem; // will return NULL if no more parents
  }
  else
   return CTreeCtrl::GetNextItem(hItem, nCode); // standard processing
}

///////////////////////////////////////////////////////////////////////////////
// Helpers to list out selected items. (Use similar to GetFirstVisibleItem(),
// GetNextVisibleItem() and GetPrevVisibleItem()!)

HTREEITEM CTreeCtrlEx::GetFirstSelectedItem()
{
  for ( HTREEITEM hItem = GetRootItem(); hItem!=NULL; hItem = GetNextVisibleItem( hItem ) )
   if ( GetItemState( hItem, TVIS_SELECTED ) & TVIS_SELECTED )
    return hItem;

 return NULL;
}

HTREEITEM CTreeCtrlEx::GetNextSelectedItem( HTREEITEM hItem )
{
  for ( hItem = GetNextVisibleItem( hItem ); hItem!=NULL; hItem = GetNextVisibleItem( hItem ) )
   if ( GetItemState( hItem, TVIS_SELECTED ) & TVIS_SELECTED )
    return hItem;

 return NULL;
}

HTREEITEM CTreeCtrlEx::GetPrevSelectedItem( HTREEITEM hItem )
{
  for ( hItem = GetPrevVisibleItem( hItem ); hItem!=NULL; hItem = GetPrevVisibleItem( hItem ) )
   if ( GetItemState( hItem, TVIS_SELECTED ) & TVIS_SELECTED )
    return hItem;

 return NULL;
}


///////////////////////////////////////////////////////////////////////////////
// Select/unselect item without unselecting other items

BOOL CTreeCtrlEx::SelectItemEx(HTREEITEM hItem, BOOL bSelect/*=TRUE*/)
{
  HTREEITEM hSelItem = GetSelectedItem();

 if ( hItem==hSelItem )
  {
   if ( !bSelect )
   {
    SelectItem( NULL );
    return TRUE;
   }

  return FALSE;
  }

 SelectItem( hItem );
  m_hFirstSelectedItem=hItem;

 // Reselect previous "real" selected item which was unselected byt SelectItem()
  if ( hSelItem )
   SetItemState( hSelItem, TVIS_SELECTED, TVIS_SELECTED );

 return TRUE;
}

///////////////////////////////////////////////////////////////////////////////
// Select visible items between specified 'from' and 'to' item (including these!)
// If the 'to' item is above the 'from' item, it traverses the tree in reverse
// direction. Selection on other items is cleared!

BOOL CTreeCtrlEx::SelectItems( HTREEITEM hFromItem, HTREEITEM hToItem )
{
  // Determine direction of selection
  // (see what item comes first in the tree)
  HTREEITEM hItem = GetRootItem();

 while ( hItem && hItem!=hFromItem && hItem!=hToItem )
   hItem = GetNextVisibleItem( hItem );

 if ( !hItem )
   return FALSE; // Items not visible in tree

 BOOL bReverse = hItem==hToItem;

 // "Really" select the 'to' item (which will deselect
  // the previously selected item)

 SelectItem( hToItem );

 // Go through all visible items again and select/unselect

 hItem = GetRootItem();
  BOOL bSelect = FALSE;

 while ( hItem )
  {
   if ( hItem == ( bReverse ? hToItem : hFromItem ) )
    bSelect = TRUE;

  if ( bSelect )
   {
    if ( !( GetItemState( hItem, TVIS_SELECTED ) & TVIS_SELECTED ) )
     SetItemState( hItem, TVIS_SELECTED, TVIS_SELECTED );
   }
   else
   {
    if ( GetItemState( hItem, TVIS_SELECTED ) & TVIS_SELECTED )
     SetItemState( hItem, 0, TVIS_SELECTED );
   }

  if ( hItem == ( bReverse ? hFromItem : hToItem ) )
    bSelect = FALSE;

  hItem = GetNextVisibleItem( hItem );
  }

 return TRUE;
}


///////////////////////////////////////////////////////////////////////////////
// Clear selected state on all visible items

void CTreeCtrlEx::ClearSelection(BOOL bMultiOnly/*=FALSE*/)
{
// if ( !bMultiOnly )
//  SelectItem( NULL );

 for ( HTREEITEM hItem=GetRootItem(); hItem!=NULL; hItem=GetNextVisibleItem( hItem ) )
   if ( GetItemState( hItem, TVIS_SELECTED ) & TVIS_SELECTED )
    SetItemState( hItem, 0, TVIS_SELECTED );
}


///////////////////////////////////////////////////////////////////////////////
// If a node is collapsed, we should clear selections of its child items

BOOL CTreeCtrlEx::OnItemexpanding(NMHDR* pNMHDR, LRESULT* pResult)
{
  NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;

 if ( pNMTreeView->action == TVE_COLLAPSE )
  {
   HTREEITEM hItem = GetChildItem( pNMTreeView->itemNew.hItem );

  while ( hItem )
   {
    if ( GetItemState( hItem, TVIS_SELECTED ) & TVIS_SELECTED )
     SetItemState( hItem, 0, TVIS_SELECTED );

   // Get the next node: First see if current node has a child
    HTREEITEM hNextItem = GetChildItem( hItem );
    if ( !hNextItem )
    {
     // No child: Get next sibling item
     if ( !( hNextItem = GetNextSiblingItem( hItem ) ) )
     {
      HTREEITEM hParentItem = hItem;
      while ( !hNextItem )
      {
       // No more children: Get parent
       if ( !( hParentItem = GetParentItem( hParentItem ) ) )
        break;

      // Quit when parent is the collapsed node
       // (Don't do anything to siblings of this)
       if ( hParentItem == pNMTreeView->itemNew.hItem )
        break;

      // Get next sibling to parent
       hNextItem = GetNextSiblingItem( hParentItem );
      }

     // Quit when parent is the collapsed node
      if ( hParentItem == pNMTreeView->itemNew.hItem )
       break;
     }
    }

   hItem = hNextItem;
   }
  }
 
  *pResult = 0;
  return FALSE; // Allow parent to handle this notification as well
}


///////////////////////////////////////////////////////////////////////////////
// Intercept TVN_SELCHANGED and pass it only to the parent window of the
// selection process is finished

BOOL CTreeCtrlEx::OnSelchanged(NMHDR* pNMHDR, LRESULT* pResult)
{
  // Return TRUE if selection is not complete. This will prevent the
  // notification from being sent to parent.
  return !m_bSelectionComplete; 
}


///////////////////////////////////////////////////////////////////////////////
// Ensure the multiple selected items are drawn correctly when loosing/getting
// the focus

BOOL CTreeCtrlEx::OnSetfocus(NMHDR* pNMHDR, LRESULT* pResult)
{
  Invalidate();
  *pResult = 0;
  return FALSE;
}

BOOL CTreeCtrlEx::OnKillfocus(NMHDR* pNMHDR, LRESULT* pResult)
{
  Invalidate();
  *pResult = 0;
  return FALSE;
}

void CTreeCtrlEx::OnLButtonDblClk(UINT nFlags, CPoint point)
{
  // We stop label editing.
  m_bEditLabelPending = FALSE;
  CTreeCtrl::OnLButtonDblClk(nFlags, point);
}

void CTreeCtrlEx::OnTimer(UINT nIDEvent)
{
  if (nIDEvent == TCEX_EDITLABEL)
  {
   // Stop the timer.
   KillTimer(m_idTimer);

  // Invoke label editing.
   if (m_bEditLabelPending)
    EditLabel(GetSelectedItem());

  m_bEditLabelPending = FALSE;
   return;
  }

 CTreeCtrl::OnTimer(nIDEvent);
}

///////////////////////////////////////////////////////////////////////////////
// Retreives a tree ctrl item given the item's data

HTREEITEM CTreeCtrlEx::ItemFromData(DWORD dwData, HTREEITEM hStartAtItem/*=NULL*/) const
{
  // Traverse all items in tree control
  HTREEITEM hItem;
  if ( hStartAtItem )
   hItem = hStartAtItem;
  else
   hItem = GetRootItem();

 while ( hItem )
  {
   if ( dwData == (DWORD)GetItemData( hItem ) )
    return hItem;

  // Get first child node
   HTREEITEM hNextItem = GetChildItem( hItem );

  if ( !hNextItem )
   {
    // Get next sibling child
    hNextItem = GetNextSiblingItem( hItem );

   if ( !hNextItem )
    {
     HTREEITEM hParentItem=hItem;
     while ( !hNextItem && hParentItem )
     {
      // No more children: Get next sibling to parent
      hParentItem = GetParentItem( hParentItem );
      hNextItem = GetNextSiblingItem( hParentItem );
     }
    }
   }

  hItem = hNextItem;
  }

 return NULL;
}


/////////////////////////////////////////////////////////////////////////////
// Global function to retreive a HTREEITEM from a tree control, given the
// item's itemdata.

HTREEITEM GetTreeItemFromData(CTreeCtrl& treeCtrl, DWORD dwData, HTREEITEM hStartAtItem /*=NULL*/)
{
  // Traverse from given item (or all items if hFromItem is NULL)
  HTREEITEM hItem;
  if ( hStartAtItem )
   hItem=hStartAtItem;
  else
   hItem = treeCtrl.GetRootItem();

 while ( hItem )
  {
   if ( dwData == (DWORD)treeCtrl.GetItemData( hItem ) )
    return hItem;

  // Get first child node
   HTREEITEM hNextItem = treeCtrl.GetChildItem( hItem );

  if ( !hNextItem )
   {
    // Get next sibling child
    hNextItem = treeCtrl.GetNextSiblingItem( hItem );

   if ( !hNextItem )
    {
     HTREEITEM hParentItem=hItem;
     while ( !hNextItem && hParentItem )
     {
      // No more children: Get next sibling to parent
      hParentItem = treeCtrl.GetParentItem( hParentItem );
      hNextItem = treeCtrl.GetNextSiblingItem( hParentItem );
     }
    }
   }
   hItem = hNextItem;
  }
  return NULL;
}


关于我们 版权声明 广告服务 联系我们 友情链接 加入收藏
站长:施昌权    Email:scq2099yt@163.com    MSN:scq2099yt@live.cn    QQ:14046300    本站QQ群:67202409
Copyright © 2008     卓为VC(www.joyvc.cn)    All Rights Reserved    建议分辨率 1024×768
本站由施昌权制作维护
京ICP备09012297号