ツリーアイテムのドラッグ&ドロップ

ドラッグ&ドロップによってツリーアイテムを自由に移動することができる、 ツリーコントロールクラスの作成方法は以下のとおりである。

新しいクラスCMyTreeCtrlを、基本クラスをCTreeCtrlとして作成する。
クラスを作成したら、ヘッダーファイルに以下のようなメンバ変数を定義する。

	class CMyTreeCtrl : public CTreeCtrl
	{
	// コンストラクション
	public:
		CMyTreeCtrl();
	……
	private:
		CImageList*  m_pDragImage;  // ドラッグイメージを格納するポインタ
		BOOL         m_bDrag;       // ドラッグ中ならTRUE、ドラッグ中でなければFALSE
		HTREEITEM    m_hitemDrag;   // ドラッグアイテム(コピー元アイテム)のハンドル
	};
次にソースファイルを開き、コンストラクタでメンバ変数の初期化処理をおこなう。
	// コンストラクタ
	CMyTreeCtrl::CMyTreeCtrl()
	{
		m_pDragImage = NULL;
		m_bDrag = FALSE;
		m_hitemDrag = NULL;
	}
ClassWizardを使い、TVN_BEGINDRAG、WM_MOUSEMOVE、WM_LBUTTONUPメッセージのハンドラ関数を定義する。 また、新しくCopyItemという関数を定義する。 それぞれの関数の定義内容は以下のとおりである。
	// TVN_BEGINDRAGメッセージのハンドラ
	void CMyTreeCtrl::OnBegindrag(NMHDR* pNMHDR, LRESULT* pResult) 
	{
		NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;

		// ドラッグするツリーアイテムのハンドルを取得
		m_hitemDrag = HitTest(pNMTreeView->ptDrag, NULL);

		// ドラッグイメージ(イメージリスト)の作成
		if( !(m_pDragImage = CreateDragImage(m_hitemDrag)) )
		{
			return;
		}

		// ドラッグ中フラグをONし、CImageList::BeginDrag関数を実行
		m_bDrag = TRUE;
		m_pDragImage->BeginDrag(0, CPoint(0, 0));

		// CImageList::DragEnter関数を実行(座標はスクリーン座標にする必要あり)
		POINT pt = pNMTreeView->ptDrag;
		ClientToScreen( &pt );
		m_pDragImage->DragEnter(NULL, pt);

		// マウスキャプチャー
		SetCapture();
	
		*pResult = 0;
	}

	// WM_MOUSEMOVEメッセージのハンドラ
	void CMyTreeCtrl::OnMouseMove(UINT nFlags, CPoint point) 
	{
		if (m_bDrag)
		{
			HTREEITEM hItem = HitTest(point, NULL);

			// CImageList::DragMove関数を実行(座標はスクリーン座標にする必要あり)
			POINT pt = point;
			ClientToScreen( &pt );
			CImageList::DragMove(pt);

			// ドロップ先を強調表示する
			if ( hItem )
			{
				CImageList::DragShowNolock(FALSE);
				SelectDropTarget(hItem);
				CImageList::DragShowNolock(TRUE);
			}
		}

		CTreeCtrl::OnMouseMove(nFlags, point);
	}

	// WM_LBUTTONUPメッセージのハンドラ
	void CMyTreeCtrl::OnLButtonUp(UINT nFlags, CPoint point) 
	{
		CTreeCtrl::OnLButtonUp(nFlags, point);

		if (m_bDrag)
		{
			// ドラッグ中フラグをOFFし、マウスキャプチャーを解放
			m_bDrag = FALSE;
			ReleaseCapture();

			// ドロップ先ツリーアイテムのハンドルを取得
			HTREEITEM hItemDrop = HitTest(point, NULL);

			// CImageList::DragLeave関数とCImageList::EndDrag関数を実行
			CImageList::DragLeave(this);
			CImageList::EndDrag();

			// ドラッグイメージの破棄
			if( m_pDragImage )
			{
				delete m_pDragImage;
				m_pDragImage = NULL;
			}

			// ドロップ先の強調表示を解除
			SelectDropTarget(NULL);

			// ドロップ先が存在しない場合は何もしない
			// ドラッグアイテムとドロップ先アイテムが等しい場合は何もしない
			if( hItemDrop == NULL || m_hitemDrag == hItemDrop )
			{
				return;
			}

			// ドロップ先がドラッグアイテムの子孫の場合は何もしない
			HTREEITEM hItemParent = hItemDrop;
			while( (hItemParent = GetParentItem( hItemParent )) != NULL )
			{
				if( hItemParent == m_hitemDrag )
				{
					return;
				}
			}

			// アイテムをコピーし、コピー元アイテムを削除
			CopyItem(m_hitemDrag, hItemDrop, TVI_SORT);
			DeleteItem(m_hitemDrag);
		}
	}

	// ツリーアイテムをドロップ先へコピーする関数
	HTREEITEM CMyTreeCtrl::CopyItem(
		HTREEITEM hItem, HTREEITEM hParent, HTREEITEM hInsertAfter)
	{
		// コピー元アイテムの情報をTVITEM構造体に取得する
		TVITEM tvItem;
		tvItem.hItem = hItem;
		tvItem.mask = TVIF_CHILDREN | TVIF_STATE | TVIF_PARAM |
			TVIF_HANDLE | TVIF_IMAGE | TVIF_SELECTEDIMAGE;
		GetItem(&tvItem);

		// コピー元アイテムのテキスト情報をTVITEM構造体に取得する
		CString strText = GetItemText(hItem);
		tvItem.cchTextMax = strText.GetLength();
		tvItem.pszText = (LPTSTR)(LPCTSTR)strText;

		// アイテム挿入時のマスクを設定
		tvItem.mask = TVIF_STATE | TVIF_PARAM | TVIF_TEXT |
			TVIF_HANDLE | TVIF_IMAGE | TVIF_SELECTEDIMAGE;

		// TVINSERTSTRUCT構造体を用意し、値を設定
		TVINSERTSTRUCT tvInsert;
		tvInsert.hParent = hParent;
		tvInsert.hInsertAfter = hInsertAfter;
		tvInsert.item = tvItem;

		// コピー先の親アイテム下に新しいアイテムを挿入
		HTREEITEM hRetItem = InsertItem(&tvInsert);

		// コピー元アイテムが子孫を持つ場合にはそれらもコピーする必要がある
		HTREEITEM hChild = GetChildItem(hItem);
		while( hChild )
		{
			CopyItem(hChild, hRetItem);
			hChild = GetNextSiblingItem(hChild);
		}

		return hRetItem;
	}

目次へ