首页 > 代码库 > CTreeCtrl结点拖动实现(与后台联动)

CTreeCtrl结点拖动实现(与后台联动)

原帖及讨论:http://bbs.bccn.net/thread-211413-1-1.html

效果描述:鼠标点击并拖动某一结点可以把它以动到其他结点下。
原理:把一个结点机器下面的所有结点在需要释放的位置拷贝,释放后再把原来位置的结点删掉,结点拖动主要用到三个系统消息。
1.    OnBeginDrag:选中要拖动的结点,建立拖动阴影(即拖动时和鼠标一起移动的那个阴影图标)
2.    OnMouseMove:将拖动阴影与鼠标绑定,使其同鼠标一起移动。
3.    LButtonUp:鼠标释放,结点拷贝,删除
如果单纯实现页面效果以上就足够了,但是如果要和后台联动,尝试在不同的情况下变幻图表或文字那还要自己写几个函数。这里提空两个函数例子,MoveBarch:用于移动结点。OnConnectStateCall:改变图标
实现步骤:
结合函数说明

void CHvrTree::OnBegindrag(NMHDR* pNMHDR, LRESULT* pResult) 
{

    CPoint pt;

    ST_TREE_NODE *pDragNode = NULL;

    NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;

    g_pLogger->Debug( "<%s(%d)> OnBegindrag(): Enter", D_PC_LOG_NAME_HVRTREE, __LINE__ );    
    //////////////////////////////////////////////////////////////////////////
    // 要移动的结点
    m_hItemDragS = pNMTreeView->itemNew.hItem;

    // 建立结点图标列
    m_pDragImage = CreateDragImage( m_hItemDragS );
    if( m_pDragImage == NULL )
    {
        // 错误提示在Log中
        g_pLogger->Error( "<%s(%d)> OnBegindrag(): トラックイメージ取得失敗, err - %d", 
                    D_PC_LOG_NAME_HVRTREE, __LINE__, GetLastError() );    

        g_pLogger->Debug( "<%s(%d)> OnBegindrag(): Leave", D_PC_LOG_NAME_HVRTREE, __LINE__ );    

        return;
    }

    //////////////////////////////////////////////////////////////////////////
    // 拖动开始,首先县判断要拖动的结点是否存在
    if( m_mapNode.Lookup( m_hItemDragS, (void*&)pDragNode ) == FALSE ) 
    {       
        // 取得しない場合

        // エラーログ
        g_pLogger->Error( "<%s(%d)> OnBegindrag(): ノード情報取得失敗, err - %d", 
                    D_PC_LOG_NAME_HVRTREE, __LINE__, GetLastError() );    

        g_pLogger->Debug( "<%s(%d)> OnBegindrag(): Leave", D_PC_LOG_NAME_HVRTREE, __LINE__ );    
    }

    // 拖动检验,采用储存在映射表中的结点类型来分类可拖动和不可拖动的结点
    // 2段カテゴリノード、物件ノードとカメラノードのみトラック許可
    if( (pDragNode->nType == D_PC_NODE_CATE_1)||(pDragNode->nType == D_PC_NODE_CATE_2)
        || (pDragNode->nType == D_PC_NODE_HVR) 
        || (pDragNode->nType == D_PC_NODE_CAM) ) 
    {
        //////////////////////////////////////////////////////////////////////////
        // トラック開始
        m_bDragging = TRUE;                         // 拖动标志位

        m_pDragImage->BeginDrag( 0, CPoint(8,8) );  // 开始拖动的图标索引和图标位置的设定

        pt = pNMTreeView->ptDrag;                   // 拖动起点位置

        ClientToScreen( &pt );

        m_pDragImage->DragEnter( AfxGetMainWnd(), pt );  // 在指定的垫上显示图标阴影

        SetCapture();                               // 获得鼠标焦点

        SetTimer( D_PC_TIMER_HVRTREE_SCROLL, D_PC_TIMER_HVRTREE_SCROLL_TIME_OUT, NULL ); // 这个计时器用来设定当拖动的结点到达空间边缘却没有被释放时则该控件自动上移或下移
    }

    g_pLogger->Debug( "<%s(%d)> OnBegindrag(): Leave", D_PC_LOG_NAME_HVRTREE, __LINE__ );    

    *pResult = 0;
}

void CHvrTree::OnMouseMove(UINT nFlags, CPoint point) 
{

    HTREEITEM hItem;    // 目標項目判断
    UINT      flags;
    CString   DeviceName;
    
    if(m_bDragging)
    {

        // トラック状態
        CPoint  pt = point;
        CRect rcHvrInfoView, rcMainFrame;

        AfxGetMainWnd()->GetWindowRect(&rcMainFrame);
        ClientToScreen(&rcMainFrame);
        GetWindowRect(&rcHvrInfoView);        
        ClientToScreen(&rcHvrInfoView);

        pt.x += rcHvrInfoView.left-rcMainFrame.left;
        pt.y += rcHvrInfoView.top-rcMainFrame.top;

        CImageList::DragMove(pt);
        CImageList::DragShowNolock(false);  

        // 移动点判断。在控件内时如果移动到该结点的父结点则鼠标变为不可释放图标
        if( ( hItem = HitTest( point, &flags ) ) != NULL ) 
        {
            // 元の目標設定
            if ( ::LoadCursor(NULL, IDC_NO) != ::GetCursor() )
            {
                // アイコー設定
                SetCursor(::LoadCursor(NULL, IDC_NO));

                SelectDropTarget(NULL);
            }
            // トラップ目標設定
            if ( GetParentItem(m_hItemDragS) != hItem ) 
            {
                // 鼠标图标设定
                if (::LoadCursor(NULL, IDC_ARROW) != ::GetCursor()) ::SetCursor(::LoadCursor(NULL, IDC_ARROW)); 
            
                SelectDropTarget(hItem);
            }
            m_hItemDragD = hItem;
        }
        // 在控件外时,鼠标图标也作相应处理
        hItem = HitTest( point, &flags );
        
        if( hItem == NULL ) 
        {
            ::SetCursor(::LoadCursor(NULL, IDC_ARROW)); 
        }

        CImageList::DragShowNolock(true);
    }

    CTreeCtrl::OnMouseMove(nFlags, point);
}
// 这里是拖动的重要环节。因为涉及到众多状态判断,所以大胆使用了goto,因为只在一个函数内使用所影响还不是很大。要是那位有更好的办法请赐教
void CHvrTree::OnLButtonUp(UINT nFlags, CPoint point) 
{
    BOOL bSuc;

    ST_TREE_NODE *pNode = NULL;             // 映射表中的结构初始化 

    ST_TREE_NODE *pDestNode = NULL;         // 目標ノード情報

    ST_TREE_NODE *pParentNode = NULL;       // 親ノード情報

    ST_DRAG_NODE_INFO *pDragInfo = NULL;    // トラック情報 

    HTREEITEM hItem;    // 目標項目判断
    
    UINT nFlagsEx;

    HTREEITEM hChild;
    
    ST_TREE_NODE *pChildNode = NULL;
    
    INT nRet;

    HTREEITEM hNewDragItem;                    // 移动后的结点

    g_pLogger->Debug( "<%s(%d)> OnLButtonUp: Enter", D_PC_LOG_NAME_HVRTREE, __LINE__ );

    //SelectDropTarget(NULL);    // 结点不要重画,否则会出现不规则的蓝条,原因是拖动释放的位置不见得就正好是要拖放位置的左上角,如果这时重画节点就会出现描画不完全的情况

    SetItemState(m_hOld,0,TVIS_SELECTED|LVIS_FOCUSED);

    // 拖放标志位。要不是执行拖动操作就直接退出
    if( !m_bDragging ) 
    {       
        goto LBTNUP;
    }

    //////////////////////////////////////////////////////////////////////////
    // トラック中止

    m_bDragging = FALSE;            // トラック状態変更

    CImageList::DragLeave(this);    // ロック解除

    CImageList::EndDrag();          // トラック終了

    ReleaseCapture();               // マウスイベントリリース

    // トラックイメージをクリア
    delete m_pDragImage;

    m_pDragImage = NULL;

    KillTimer( D_PC_TIMER_HVRTREE_SCROLL ); // スクロールタイマ削除

    SelectDropTarget(NULL);    // ドラッグ&ドロップされたノードを描面しない

    //////////////////////////////////////////////////////////////////////////

    // ノード情報取得
    bSuc = m_mapNode.Lookup( m_hItemDragS, (void*&)pNode ); 

    if( bSuc == FALSE ) 
    {
        // エラーログ
        g_pLogger->Error( "<%s(%d)> OnLButtonUp: 始点ノード情報取得できない, hItem - %08x",
                        D_PC_LOG_NAME_HVRTREE, __LINE__, m_hItemDragS );

        goto LBTNUP;
    }

    // 目標項目判断
    if( hItem = HitTest( point, &nFlagsEx ) ) 
    {
        // 权限判断。小于level4的用户不能拖动
        if( g_hvrUser.GetUserLevel() < D_PC_USER_LEVEL_4 ) 
        {    
            MsgBox( this, D_PC_ERR_STR_HVRTREE_LEVEL_CHECK_FAIL, D_PC_ERR_STR_HVRTREE_MOVE_FAIL, MB_ICONERROR, MB_OK );
            goto LBTNUP;
        }

        // リストに移動する

        if( hItem == m_hItemDragS ) 
        {
            // 如果移动原来的父结点下面则不作处理
            SetItemState(hItem,0,TVIS_SELECTED);

            SetItemState(GetParentItem(hItem),0,TVIS_SELECTED);

            SelectDropTarget(hItem); 

            goto LBTNUP;
        }

        // 结点类型判断,这主要是因为要求中有些结点有不同的动作
        if( pNode->nType != D_PC_NODE_CATE_2 && pNode->nType != D_PC_NODE_HVR && pNode->nType != D_PC_NODE_CATE_1 ) 
        {
            // 1段カテゴリ、2段カテゴリ、物件ノードのみ、移動できる

            goto LBTNUP;
        }

        // 目標ノード情報取得
        bSuc = m_mapNode.Lookup( hItem, (void*&)pDestNode );
        if( bSuc == FALSE ) 
        {
            // 目標ノード情報取得できない
            g_pLogger->Info( "<%s(%d)> OnLButtonUp: 目標ノード情報取得できない, hItem - %08x",
                        D_PC_LOG_NAME_HVRTREE, __LINE__, hItem );

            goto LBTNUP;
        }
        // 選択をノード
        SelectItem(hItem);
            // ノード移動
        if( (pNode->nType == D_PC_NODE_CATE_2) && (pDestNode->nType == D_PC_NODE_CATE_1) ) 
        {
            // 2段カテゴリノード→1段カテゴリノードに移動
            g_pLogger->Info( "<%s(%d)> OnLButtonUp: 2段カテゴリノード→1段カテゴリノードに移動, 始点ノード - %08x,終点ノード - %08x",
                        D_PC_LOG_NAME_HVRTREE, __LINE__, m_hItemDragS,hItem );

            hNewDragItem = MoveBranch( m_hItemDragS, hItem, TVI_LAST );
        } 
        // 1段カテゴリノード→1段カテゴリノードに移動
        else if( (pNode->nType == D_PC_NODE_CATE_1) && (pDestNode->nType == D_PC_NODE_CATE_1) ) 
        {
            hChild = GetChildItem(pNode->hItem);
            while( hChild != NULL)
            {
                nRet = m_mapNode.Lookup( hChild,(void*&)pChildNode );
                if ( nRet == D_PC_FAILURE) 
                {
                    // エラーログ
                    g_pLogger->Error( "<%s(%d)> OnLButtonUp(): 子のノード情報取得できない, hItem - %08x", 
                                D_PC_LOG_NAME_HVRTREE, __LINE__, hChild );

                    goto LBTNUP;
                }
                if ( pChildNode->nType == D_PC_NODE_CATE_2 )
                {
                    goto LBTNUP;
                }
                hChild = GetNextVisibleItem(hChild);
            }

            // 1段カテゴリノード→1段カテゴリノードに移動
            g_pLogger->Info( "<%s(%d)> OnLButtonUp: 1段カテゴリノード→1段カテゴリノードに移動, 始点ノード - %08x,終点ノード - %08x",
                        D_PC_LOG_NAME_HVRTREE, __LINE__, m_hItemDragS,hItem );

            hNewDragItem = MoveBranch( m_hItemDragS, hItem, TVI_LAST );
        } 
        // 2段カテゴリノード→ROOTノードに移動
        else if( (pNode->nType == D_PC_NODE_CATE_2) && (pDestNode->nType == D_PC_NODE_ROOT) ) 
        {
            // 2段カテゴリノード→1段カテゴリノードに移動
            g_pLogger->Info( "<%s(%d)> OnLButtonUp: 2段カテゴリノード→Rootノードに移動, 始点ノード - %08x,終点ノード - %08x",
                        D_PC_LOG_NAME_HVRTREE, __LINE__, m_hItemDragS,hItem );

            hNewDragItem = MoveBranch( m_hItemDragS, hItem, TVI_LAST );
        } 

        else if( (pNode->nType == D_PC_NODE_HVR) && (pDestNode->nType >= D_PC_NODE_ROOT)
            && (pDestNode->nType <= D_PC_NODE_CATE_2) ) 
        {
            // 物件ノード移動
            g_pLogger->Info( "<%s(%d)> OnLButtonUp: 物件ノード移動, 始点ノード - %08x,終点ノード - %08x",
                        D_PC_LOG_NAME_HVRTREE, __LINE__, m_hItemDragS,hItem );

            hNewDragItem = MoveBranch( m_hItemDragS, hItem, TVI_LAST );
        }

        // ツリー更新した
        m_bDirty = TRUE;

        // 情報ログ
        g_pLogger->Info( "<%s(%d)> OnLButtonUp: ノード移動 \r\n\
                        src item - %08x, parent - %08x, type - %d, Id - %d, conn - %d, err - %d\r\n\
                        dest item - %08x, parent - %08x, type - %d, Id - %d, conn - %d, err - %d", 
                        D_PC_LOG_NAME_HVRTREE, __LINE__,
                        pNode->hItem, pNode->hParentHItem, pNode->nType, pNode->Id, pNode->nConnectHvrNum, pNode->nErrHvrNum,
                        pDestNode->hItem, pDestNode->hParentHItem, pDestNode->nType, pDestNode->Id,
                        pDestNode->nConnectHvrNum, pDestNode->nErrHvrNum );
    } 
    else 
    {
        //////////////////////////////////////////////////////////////////////////
        // 将结点移出控件外的操作,主要是把需要的信息通过消息在指定的坐标处传递出去
        if( pNode->nType != D_PC_NODE_HVR && pNode->nType != D_PC_NODE_CAM ) 
        {           
            // カテゴリなど移動すれば、無効な操作
            goto LBTNUP;
        }

        // トラック情報作成
        pDragInfo = new ST_DRAG_NODE_INFO;
        if( pDragInfo == NULL ) 
        {          
            // エラーログ
            g_pLogger->Error( "<%s(%d)> OnLButtonUp: メモリ配分エラー",
                        D_PC_LOG_NAME_HVRTREE, __LINE__ );
    
            goto LBTNUP;

        }
        
        // トラック情報設定
        memset( pDragInfo, 0, sizeof( ST_DRAG_NODE_INFO ) );

        pDragInfo->uType = pNode->nType;                // トラックノードタイプ

        ClientToScreen( &point );                       // →スクリーン座標

        AfxGetMainWnd()->ScreenToClient( &point );      // →メイン画面のクレント座標

        pDragInfo->nX = point.x;                        // 水平座標点

        pDragInfo->nY = point.y;                        // 垂直座標点

        g_pLogger->Info( "<%s(%d)> OnLButtonUp: リスト以外区域移動、水平座標点 - %d,垂直座標点 - %d"
            , D_PC_LOG_NAME_HVRTREE, __LINE__ ,pDragInfo->nX,pDragInfo->nY );

        if( pNode->nType == D_PC_NODE_HVR ) 
        {
            // 物件ノード移動
            pDragInfo->lHvrId = pNode->Id;              // 物件登録番号    
        } 
        else 
        {           
            // カメラノード移動

            // 親ノード情報取得
            ASSERT( m_mapNode.Lookup( pNode->hParentHItem, (void*&)pParentNode ) == TRUE );

            pDragInfo->lHvrId = pParentNode->Id;        // 物件登録番号

            pDragInfo->uCamId = (USHORT)pNode->Id;      // カメラ番号

        }

        g_pLogger->Info( "<%s(%d)> OnLButtonUp: WM_NODE_DRAG?メイン画面pDragInfo情報\n\
            ,\t-uType - %d\r\n\
            ,\t-nX - %d\r\n\
            ,\t-nY - %d\r\n\
            ,\t-lHvrId - %d\r\n\
            ,\t-uCamId - %d\r\n"
            ,D_PC_LOG_NAME_HVRTREE, __LINE__
            ,pDragInfo->uType
            ,pDragInfo->nX
            ,pDragInfo->nY
            ,pDragInfo->lHvrId
            ,pDragInfo->uCamId);
        // WM_NODE_DRAG?メイン画面
        AfxGetMainWnd()->PostMessage( WM_NODE_DRAG, (UINT)pDragInfo, NULL );
    }
    //////////////////////////////////////////////////////////////////////////
    // 结点排序
    SetItemData(hNewDragItem,(DWORD)hNewDragItem);        // ノード情報設定

    HTREEITEM hParNewDrag;
    hParNewDrag = GetParentItem(hNewDragItem);

    nRet = SortItem( hParNewDrag );
    if ( nRet != D_PC_SUCCESS )
    {
        // エラーログ
        g_pLogger->Error("<%s(%d)> OnLButtonUp(): ソーティングカテゴリ失敗,hItem - %08x",
                    D_PC_LOG_NAME_HVRTREE, __LINE__ ,hParNewDrag );

           g_pLogger->Debug("<%s(%d)> OnLButtonUp(): Leave",D_PC_LOG_NAME_HVRTREE, __LINE__);    

        return; 
    }
    else
    {
        g_pLogger->Info("<%s(%d)> OnLButtonUp(): ソーティングカテゴリ成功",
                    D_PC_LOG_NAME_HVRTREE, __LINE__ );
    }
LBTNUP:
    
    g_pLogger->Debug( "<%s(%d)> OnLButtonUp: Leave", D_PC_LOG_NAME_HVRTREE, __LINE__ );

    CTreeCtrl::OnLButtonUp(nFlags, point);
}
// 递归函数
HTREEITEM CHvrTree::MoveBranch(HTREEITEM hSrc, HTREEITEM hDest, HTREEITEM hAfter)
{
    INT nRet;

    HTREEITEM hParent;      // 親ツリーノード

    HTREEITEM hChild;       // サッブツリーノード

    HTREEITEM hNewItem;     // 移動先ツリーノード

    CHAR szBuf[D_PC_MAC_MAX_CATE_NAME_LENGTH + 1];

    ST_TREE_INFO treeInfo;  // 該当ノードツリー情報

    ST_HVR_INFO  hvrInfo;   // 該当ノード物件情報
    
    g_pLogger->Debug( "<%s(%d)> MoveBranch(): Enter", D_PC_LOG_NAME_HVRTREE, __LINE__ );
    //////////////////////////////////////////////////////////////////////////
    // 入力チェック

    // パラメータ確認
    ASSERT( hSrc != NULL );
    ASSERT( hDest != NULL );

    // ルート項目移動不可
    ASSERT( hSrc != m_hRoot );

    // 親ノード取得
    hParent = GetParentItem( hSrc );

    if( hParent == hDest ) 
    {      
        // 移動先は本のノードになった場合
        return hSrc;
    }
    ST_TREE_NODE *pNode = NULL;         // ツリーノード情報

    ST_TREE_NODE *pParentNode = NULL;   // 親ツリーノード情報
    
    ST_TREE_NODE *pDestNode = NULL;     // 移動先ツリーノード情報
    // 元のノード情報取得
    if( m_mapNode.Lookup( hSrc, (void*&)pNode ) == FALSE ) 
    {      
        // エラーログ
        g_pLogger->Error( "<%s(%d)> MoveBranch(): 元のノード情報取得できない, hItem - %08x", 
                    D_PC_LOG_NAME_HVRTREE, __LINE__, hSrc );

        g_pLogger->Debug( "<%s(%d)> MoveBranch(): Leave", D_PC_LOG_NAME_HVRTREE, __LINE__ );

        return NULL;
    }

    // Root以上のノード移動不可
    if( pNode->nType < D_PC_NODE_ROOT ) 
    {       
        // エラーログ
        g_pLogger->Error( "<%s(%d)> MoveBranch(): ROOT以上のノード移動不可, type - %d",
                    D_PC_LOG_NAME_HVRTREE, __LINE__, pNode->nType );

        g_pLogger->Debug( "<%s(%d)> MoveBranch(): Leave", D_PC_LOG_NAME_HVRTREE, __LINE__ );

        return NULL;
    }

    //////////////////////////////////////////////////////////////////////////
    // ツリーノードコピー
    hNewItem = CopyItem( hSrc, hDest, hAfter );

    //////////////////////////////////////////////////////////////////////////
    // 子ノードを移動
    hChild = GetChildItem( hSrc );    

    while( hChild != NULL )
    {
        MoveBranch( hChild, hNewItem, hAfter );
        hChild = GetChildItem( hSrc );
    }
    //////////////////////////////////////////////////////////////////////////
    // タイプチェック
    if ((pNode->nType >= D_PC_NODE_ROOT)&&(pNode->nType <= D_PC_NODE_CATE_2)) 
    {
        if( m_mapNode.Lookup( hDest, (void*&)pDestNode ) == TRUE ) 
        {
            if (( pNode->nType == D_PC_NODE_CATE_1 )&&( pDestNode->nType == D_PC_NODE_CATE_1 ))
            {
                pNode->nType = D_PC_NODE_CATE_2;
            }
            else if (( pNode->nType == D_PC_NODE_CATE_2 )&&( pDestNode->nType == D_PC_NODE_ROOT ))
            {
                pNode->nType = D_PC_NODE_CATE_1;
            }
        }
        else
        {
            // エラーログ
            g_pLogger->Error( "<%s(%d)> MoveBranch(): 移動先ノード情報取得できない, hItem - %08x", 
                        D_PC_LOG_NAME_HVRTREE, __LINE__, hSrc );

            g_pLogger->Debug( "<%s(%d)> MoveBranch(): Leave", D_PC_LOG_NAME_HVRTREE, __LINE__ );

            return NULL;
        }
    }
    //////////////////////////////////////////////////////////////////////////
    // マッピング更新
    pNode->hItem = hNewItem;

    pNode->hParentHItem = hDest;

    // マッピングから削除
    m_mapNode.RemoveKey( hSrc );

    g_pLogger->Info( "<%s(%d)> MoveBranch():m_mapNode削除:Key - %08x "
        ,D_PC_LOG_NAME_HVRTREE, __LINE__, hSrc );

    // 新しいノードがマッピングに追加
    m_mapNode.SetAt( hNewItem, pNode );

    g_pLogger->Info( "<%s(%d)> MoveBranch(): m_mapNode追加:Key - %08x、( ST_TREE_NODE )pNode - \r\n\
            \t\t-ノード - %08x\r\n\
            \t\t-親ノード - %08x\r\n\
            \t\t-ノードタイプ - %d\r\n\
            \t\t-異常接続件数 - %d\r\n\
            \t\t-正常接続件数 - %d\r\n\
            \t\t-番号 - %d\r\n"
            ,D_PC_LOG_NAME_HVRTREE, __LINE__ 
            ,hNewItem
            ,pNode->hItem
            ,pNode->hParentHItem
            ,pNode->nType
            ,pNode->nErrHvrNum
            ,pNode->nConnectHvrNum
            ,pNode->Id );
    
    // 関連情報更新
    // 1.01 : カテゴリソーティングを機能修正
    if( (pNode->nType == D_PC_NODE_CATE_1)||(pNode->nType == D_PC_NODE_CATE_2) )
    { 
        // カテゴリ番号マッピング更新
        m_mapCateNode[(WORD)pNode->Id] = hNewItem;

    } 
    // 1.01 - END
    if( pNode->nType == D_PC_NODE_HVR ) 
    {       
        // 物件番号マッピング更新
        m_mapHvrNode[(WORD)pNode->Id] = hNewItem;

    }

    // ツリーノード接続情報
    if( m_mapNode.Lookup( hDest, (void*&)pDestNode ) == TRUE ) 
    {    
        // 親ノード登録した場合、親ノードの接続情報を更新
        if( (pNode->nType == D_PC_NODE_HVR) || (pNode->nType == D_PC_NODE_CATE_2)
            || ( pNode->nType == D_PC_NODE_CATE_1) )
        {
            ASSERT( UpdateNodeConn( pDestNode, pNode->nConnectHvrNum, pNode->nErrHvrNum ) == D_PC_SUCCESS );
        } 
        else 
        {
            // エラーログ
            g_pLogger->Error( "<%s(%d)> MoveBranch(): 接続情報更新時, 間違ったツリーノードタイプ, type - %d",
                    D_PC_LOG_NAME_HVRTREE, __LINE__, pNode->nType );
        }
    } 

    // 最後、元の親ノードの接続情報更新
    if( m_mapNode.Lookup( hParent, (void*&)pParentNode ) && pDestNode != NULL )
    {
        if( (pParentNode->nType == D_PC_NODE_CATE_1) || (pParentNode->nType == D_PC_NODE_CATE_2)
            || (pParentNode->nType == D_PC_NODE_ROOT) )
        {          
            // カテゴリノードの接続情報更新
            ASSERT( UpdateNodeConn( pParentNode, - pNode->nConnectHvrNum, - pNode->nErrHvrNum ) == D_PC_SUCCESS ) ;
        }
        
    
        // 親ノードを見つけ場合、最初の移動先ノードに戻る
        if( ( pNode->nType == D_PC_NODE_CATE_1 )||( pNode->nType == D_PC_NODE_CATE_2 ) ) 
        {         
            // 2段カテゴリ

            // ツリー情報取得
            nRet = g_hvrUser.GetTreeItem( pNode->Id, &treeInfo );
            if( nRet != D_PC_SUCCESS ) 
            {
                // エラーログ
                g_pLogger->Error( "<%s(%d)> MoveBranch(): ツリー情報取得失敗, id - %d, err - %d",
                                D_PC_LOG_NAME_HVRTREE, __LINE__, pNode->Id, nRet );
            }

            memset( szBuf, 0, D_PC_MAC_MAX_CATE_NAME_LENGTH + 1);
        
            _snprintf( szBuf, D_PC_MAC_MAX_CATE_NAME_LENGTH, "%s", treeInfo.szName );

            // ツリー情報更新
                
            nRet = g_hvrUser.DragTreeItem( pNode->Id,pDestNode->Id );

            if( nRet != D_PC_SUCCESS ) 
            {
                // エラーログ
                g_pLogger->Error( "<%s(%d)> MoveBranch(): ツリー情報更新失敗, id - %d, err - %d",
                                D_PC_LOG_NAME_HVRTREE, __LINE__, pNode->Id, nRet );
            }
        }
        else if( pNode->nType == D_PC_NODE_HVR )
        {            
            // 物件ノード

            // 物件情報取得
            nRet = g_hvrUser.GetHvrInfo( pNode->Id, &hvrInfo );
            if( nRet != D_PC_SUCCESS ) 
            {
                // エラーログ
                g_pLogger->Error( "<%s(%d)> MoveBranch(): 物件情報取得失敗, id - %d, err - %d",
                                D_PC_LOG_NAME_HVRTREE, __LINE__, pNode->Id, nRet );
            }
            
            // ツリー新番号更新
            hvrInfo.lCtNo = pDestNode->Id;

            // 物件情報更新
            nRet = g_hvrUser.UpdateHvr( pNode->Id, hvrInfo );
            if( nRet != D_PC_SUCCESS ) 
            {
                // エラーログ
                g_pLogger->Error( "<%s(%d)> MoveBranch(): 物件情報更新失敗, id - %d, err - %d",
                                D_PC_LOG_NAME_HVRTREE, __LINE__, pNode->Id, nRet );
            }

        } // 情報更新済み

    } // 元の接続情報更新済み
    
    //////////////////////////////////////////////////////////////////////////
    // 元のノード削除
    BOOL bSuc = DeleteItem( hSrc );
    if( bSuc == FALSE ) 
    {      
        // エラーログ
        g_pLogger->Error( "<%s(%d)> MoveBranch(): 削除失敗, hItem - %08x",
                    D_PC_LOG_NAME_HVRTREE, __LINE__, hSrc );
    }
    else
    {
        g_pLogger->Info( "<%s(%d)> MoveBranch(): 削除成功, hItem - %08x",
                    D_PC_LOG_NAME_HVRTREE, __LINE__, hSrc );
    }
    
    // 目標ノード拡張
    Expand( hDest, TVE_EXPAND );    

       g_pLogger->Debug( "<%s(%d)> MoveBranch(): Leave", D_PC_LOG_NAME_HVRTREE, __LINE__ );

    return  hNewItem;
}

HTREEITEM CHvrTree::CopyItem(HTREEITEM hItem,HTREEITEM htiNewParent,HTREEITEM htiAfter)
{
    g_pLogger->Debug("<%s(%d)> CopyItem(): Enter",D_PC_LOG_NAME_HVRTREE, __LINE__);    

    TV_INSERTSTRUCT  tvstruct;
    HTREEITEM        hNewItem;
    CString          strHvrName;
    
    tvstruct.item.hItem = hItem;
    tvstruct.item.mask  = TVIF_CHILDREN|TVIF_HANDLE|TVIF_IMAGE|TVIF_SELECTEDIMAGE;
    GetItem( &tvstruct.item );
    strHvrName = GetItemText( hItem );
    tvstruct.item.cchTextMax = strHvrName.GetLength();
    tvstruct.item.pszText    = strHvrName.LockBuffer();

    tvstruct.hParent = htiNewParent;
    tvstruct.hInsertAfter = htiAfter;
    tvstruct.item.mask = TVIF_IMAGE|TVIF_SELECTEDIMAGE|TVIF_TEXT;
    hNewItem = InsertItem( &tvstruct );
    strHvrName.ReleaseBuffer ();

    SetItemData( hNewItem,GetItemData(hItem) );
    SetItemState( hNewItem,GetItemState(hItem,TVIS_STATEIMAGEMASK),TVIS_STATEIMAGEMASK);
    
    g_pLogger->Debug("<%s(%d)> CopyItem(): Leave",D_PC_LOG_NAME_HVRTREE, __LINE__);    

    return hNewItem;
}
变更图表状态主要是利用SetItemImage来实现
第四部分 编辑改名
实现原理:利用CTreeCtrl自带可编辑属性,和系统函数OnBeginlabeledit,OnEndlabeledit来实现。
第五部分 结点排序
SortChildren是为该结点下面的所有子节点排序,注意只是子节点,不包括子节点的子节点。
SortChildrenCB是自定义排序方式。现给出大体例子
SortChildrenCB中的TVSORTCB需要用到一个回调函数常常是难点。
INT CHvrTree::SortItem(HTREEITEM hItem)
{
    BOOL bRet;
    CHvrTree *ptree = this;   
    TVSORTCB sortpage;   

    g_pLogger->Debug("<%s(%d)> SortItem(): Enter",D_PC_LOG_NAME_HVRTREE, __LINE__);    
    //////////////////////////////////////////////////////////////////////////
    // ソーティング情報
    g_pLogger->Info("<%s(%d)> SortItem(): ソーティングノード,hItem - %08x"
        ,D_PC_LOG_NAME_HVRTREE, __LINE__,hItem );    

    sortpage.hParent = hItem;   
    sortpage.lpfnCompare = MyCompareProc;   
    sortpage.lParam = (LPARAM)ptree;
    bRet =     SortChildrenCB(&sortpage);   
    if ( bRet ) 
    {
        g_pLogger->Debug("<%s(%d)> SortItem(): Leave",D_PC_LOG_NAME_HVRTREE, __LINE__);    

        return D_PC_SUCCESS;
    }
    else
    {
        g_pLogger->Debug("<%s(%d)> SortItem(): Leave",D_PC_LOG_NAME_HVRTREE, __LINE__);    

        return D_PC_FAILURE;
    }
}

入口参数是两个在要排序的序列中的无序参数
INT CALLBACK CHvrTree::MyCompareProc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
{
    CHvrTree* pTreeCtrl = (CHvrTree*) lParamSort;

    HTREEITEM hItem1 = (HTREEITEM)lParam1;
    HTREEITEM hItem2 = (HTREEITEM)lParam2;

    // ノードのアイコンに付けられた固有の番号によって、ノードタイプ取得 
    
    INT nKind1 = pTreeCtrl->GetItemType(hItem1);
    INT nKind2 = pTreeCtrl->GetItemType(hItem2);     
    
    // 1.01 : カテゴリソーティングを機能修正
    //////////////////////////////////////////////////////////////////////////
    // ノードタイプ取得チェック
    if ( (nKind1<0)||(nKind2<0) )
    {
         g_pLogger->Error( "<%s(%d)> MyCompareProc: 目標ノードノードタイプ情報取得できない, nType - %d",
                        D_PC_LOG_NAME_HVRTREE, __LINE__, D_PC_FAILURE );

         return D_PC_ERRCODE_HVRTREE_GETTREE_FAIL;
    }
    // 1.01 - END
    
    // ノード情報取得
    BOOL bRet;
    ST_TREE_NODE *pnodeInfo1 = NULL;                // ノード1の情報
    ST_TREE_NODE *pnodeInfo2 = NULL;                // ノード2の情報

    bRet = pTreeCtrl->m_mapNode.Lookup(hItem1,(void*&)pnodeInfo1); // ノード1の情報取得 
    
    if(bRet == FALSE)
    {
        // 目標ノード情報取得できない
         g_pLogger->Error( "<%s(%d)> MyCompareProc: 目標ノード情報取得できない, hItem - %08x",
                   D_PC_LOG_NAME_HVRTREE, __LINE__, hItem1 );
         return D_PC_ERRCODE_HVRTREE_GETTREE_FAIL;
    }
    
    bRet = pTreeCtrl->m_mapNode.Lookup(hItem2,(void*&)pnodeInfo2); // ノード2の情報取得 
    
    if(bRet == FALSE)
    {
        // 目標ノード情報取得できない
         g_pLogger->Error( "<%s(%d)> MyCompareProc: 目標ノード情報取得できない, hItem - %08x",
                        D_PC_LOG_NAME_HVRTREE, __LINE__, hItem2 );
         return D_PC_ERRCODE_HVRTREE_GETTREE_FAIL;
    }

    INT nRet = NULL;                        // 戻り値

    if( nKind1 == nKind2)
    {
        // ノード1とノード2は同じノードタイプの場合

        // ノード1とノード2はカテゴリーノードの場合    
        if( nKind1 == D_PC_NODE_CATE_1)
        {
            CString strItem1,strItem2;
            // カテゴリー名取得 
            strItem1 = pTreeCtrl->GetItemText(hItem1);
            strItem2 = pTreeCtrl->GetItemText(hItem2);
            // カテゴリー名比較
            nRet = strcmp(strItem1, strItem2); 

        }

        // 1.01 : カテゴリソーティングを機能修正
        else if( nKind1 == D_PC_NODE_CATE_2)
        {
            CString strI