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

 

时间: 2024-11-03 22:03:04

CTreeCtrl结点拖动实现(与后台联动)的相关文章

使用ExtJS技术实现的拖动树结点_extjs

一.结点拖放的位置 拖放结点包含了两个动作,拖(drag)和放(drop).拖很好理解,就是将结点拖起来,拖哪一个结点的效果都是一样的.不过放结点就比较复杂了.放结点可分为如下两种情况: 追加(append)结点:如果将拖动的结点正好放在非叶子结点的上面,TreePanel组件会将这个结点移动到非叶子结点下面作为该结点的子结点.由于TreePanel的限制,叶子结点不能append. 在同一层做上下移动(above和below): 如果将拖动的结点放在叶子结点上,或放在非叶子结点的侧面,会将拖动

《人人都玩开心网:Ext JS+Android+SSH整合开发Web与移动SNS》销售排名第4,发篇ExtJS的文章(拖放树结点)庆祝下

    <人人都玩开心网:Ext JS+Android+SSH整合开发Web与移动SNS>一书上架短短几天,就进入了互动网计算机类销售总排名第4的好成绩(见下图).现发篇Ext JS的技术文章庆祝下. 拖放树结点 在本文将介绍TreePanel组件非常有意思的一个功能:结点拖放.要使TreePanel组件的结点可以拖放非常简单,只需要将TreePanel类的enableDD选项参数设为true即可.当然,要想实现更复杂的功能,还需要配合其他的参数和事件. 一.结点拖放的位置 拖放结点包含了两个

利用AJAX+J2EE开发组织机构管理系统(2)

ajax|j2ee|机构管理 <body > init方法实现如下: function init(){ //定义personDom为一个XMLDOM'对象 personDom= new ActiveXObject('Microsoft.XMLDOM'); personDom.async = false; //定义stylesheet为一个XMLDOM'对象,且stylesheet为personDom确定显示风格 stylesheet = new ActiveXObject('Microsoft

利用AJAX+J2EE开发组织机构管理系统(1)

ajax|j2ee|机构管理 一. 概述 AJAX是今年初才问世的新技术,是Asynchronous JavaScript and XML的缩写.它是一组开发Web应用程序的技术,它使浏览器可以为用户提供更为自然的浏览体验.每当需要更新时,客户端Web页面的修改是异步的和逐步增加的.这样,AJAX在提交Web页面内容时大大提高了用户界面的速度.在基于AJAX的应用程序中没有必要长时间等待整个页面的刷新.页面中需要更新的那部分才进行更改,如果可能的话,更新是在本地完成的,并且是异步的. J2ee是

AJAX+J2EE开发组织机构管理系统(1)

ajax|j2ee|机构管理 一. 概述 AJAX是今年初才问世的新技术,是Asynchronous JavaScript and XML的缩写.它是一组开发Web应用程序的技术,它使浏览器可以为用户提供更为自然的浏览体验.每当需要更新时,客户端Web页面的修改是异步的和逐步增加的. 这样,AJAX在提交Web页面内容时大大提高了用户界面的速度.在基于AJAX的应用程序中没有必要长时间等待整个页面的刷新.页面中需要更新的那部分才进行更改,如果可能的话,更新是在本地完成的,并且是异步的. J2ee

Anynote v1.3.1发布 多用户个人信息管理系统

Anynote是一个基于extjs.struts.spring.ibatis构建的开源的支持多用户的个人http://www.aliyun.com/zixun/aggregation/13617.html">信息管理系统,并且各个功能模块都可以选择性的开启或者停用,主要功能包括: 任务管理,记录工作和生活中的 待办事项,有效安排时间,支持分类.设定优先级: 日常笔记,不管是记录日常生活的点点滴滴,还是收藏一些好的网文,都是不错的选择: 阅读器,可以订阅自己感兴趣的内容,集中阅读: 个人相册

独家 | 陆化普:大数据、AI解决交通管理难题的新思路

[导读] 2017年8月24日,以助推"平安交通.和谐交通.便民交通"为导向的研讨会在贵州省公安厅交通管理局成功举办.在会上,清华大学清华-青岛数据科学研究院(以下简称:数据院).交通研究所和贵州省公安厅交通管理局的达成合作共识,共同挖掘贵州省交通大数据的价值,产生一批落地的科研成果,并应用到交通管理局实际的交通管理工作中. 数据院一直秉承开放包容的理念,积极推进大数据与各领域的跨界交流.此前数据院在交通领域举办了以大数据与新能源为主题的论坛并邀请众多专家出席,同时也支持了清华大学交通

DedeCMS自定义模型使用教程

中介交易 SEO诊断 淘宝客 云主机 技术大厅 在织梦系统中有内容模型这个概念,不同内容模型可以用来构建不同内容形式的站点,在系统中自带了以下几种模型:普通文章.图集.软件.商品.分类信息.专题.通过系统自带的模型,我们可以用来构建不同类型的站点, 例如:使用图集可以做一个图片站,用软件模型构建一个软件下载站点.        当然以上随系统附带的模型被称为系统模型,用户可以自己定义一些模型,比如图书.音乐专辑等,自定义了这些模型才可以构建更多内容形式的站点,本篇将讲述如何使用系统的自定义模型管

腾讯COO任宇昕兼任总裁的MIG如何重新上路

半年多过去了,由腾讯COO任宇昕兼任总裁的MIG,如何重新上路? 在今年年初的架构调整中,腾讯旗下移动互联网事业群(MobileInternetGroup,下称"MIG"),可谓伤筋动骨,主要的盈利性业务被剥离,三分之一的员工被调走. 此前,由于MIG的定位是承接整个公司的移动互联网,腾讯几乎所有移动互联网相关产品都在MIG,摊子很大,但是,人才储备不够.积累深度尚浅,使其虽做了很多事情,但是,精品并不多. 任宇昕掌舵后,MIG再出发,探索新路.6月13日,一位腾讯内部人士谈到MIG管