15 мая 2023 года "Исходники.РУ" отмечают своё 23-летие!
Поздравляем всех причастных и неравнодушных с этим событием!
И огромное спасибо всем, кто был и остаётся с нами все эти годы!

Главная Форум Журнал Wiki DRKB Discuz!ML Помощь проекту


Расширенный drag'n'drop

В этом примере добавлено три новых возможности. Теперь по желанию можно делать не активными заданные элементы для drag'n'drop. Вы так же можете решать является ли элемент доступным для бросания, если нет, то предоставляется альтернативный элемент этого. Курсор отражает все состояния операции drag'n'drop.
Члены-переменные, которые должны быть объявлены:


protected:
	CImageList*     m_pDragImage;
	BOOL            m_bLDragging;
	HTREEITEM       m_hitemDrag,m_hitemDrop;
	HCURSOR         m_dropCursor,m_noDropCursor;

 

m_dropCursor и m_noDropCursor инициализированы в соответствии с желаемым видом курсоров
Авторская версия OnBeginDrag(...):


void CTreeCtrlX::OnBeginDrag(NMHDR* pNMHDR, LRESULT* pResult) 
{
	NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;

	*pResult = 0;

	m_hitemDrag = pNMTreeView->itemNew.hItem;
	m_hitemDrop = NULL;
	SelectItem( m_hitemDrag );
	if (!IsDropSource(m_hitemDrag))
		return;

	// Взять список изображений для перетаскивания
	m_pDragImage = CreateDragImage(m_hitemDrag);  
	// CreateDragImage() возвращает NULL если никакого списка 
	// изображений не было связано с деревом
	if( !m_pDragImage )
		return;

	m_bLDragging = TRUE;
	m_pDragImage->BeginDrag(0, CPoint(15, 15));
	POINT pt = pNMTreeView->ptDrag;
	ClientToScreen( &pt );
	m_pDragImage->DragEnter(NULL, pt);
	SetCapture();
}

Этот метод вызывает IsDropSource(), чтобы определить возможность drag'n'drop операции для заданного элемента. Если IsDropSource() возвращает FALSE, то перетаскивание запрещено. По умолчанию IsDropSource() всегда возвращает TRUE.
Авторская версия обработчика OnMouseMove():


void CTreeCtrlX::OnMouseMove(UINT nFlags, CPoint point) 
{
	HTREEITEM hitem;
	UINT flags;

	if (m_bLDragging)
	{
		POINT pt = point;
		ClientToScreen( &pt );
		CImageList::DragMove(pt);
		if ((hitem = HitTest(point, &flags)) != NULL)
		{
			CImageList::DragShowNolock(FALSE);
			m_hitemDrop = GetDropTarget(hitem);
			SelectDropTarget(m_hitemDrop);
			CImageList::DragShowNolock(TRUE);
		}
		else
			m_hitemDrop = NULL;
		if (m_hitemDrop)
			SetCursor(m_dropCursor);
		else
			SetCursor(m_noDropCursor);
	}

	CTreeCtrl::OnMouseMove(nFlags, point);
}

Вызов этой версии GetDropTarget() решает лигитимность цели бросания, находящейся под курсором. Она возвращает NULL, если элемент не может принять бросок; HTREEITEM, если может. Как альтернатива HTREEITEM может быть возвращено, определяя разные цели бросания. Это может быть полезно, например, если не Вы не принимаете элемент на "листья" дерева, но хотите направить его в корень.


/* virtual */ HTREEITEM CTreeCtrlX::GetDropTarget(HTREEITEM hItem)
{
	if (hItem == m_hitemDrag || hItem == GetParentItem(m_hitemDrag))
		return NULL;
	return hItem;
}

И наконец обработчик OnLButtonUp():


 void CTreeCtrlX::OnLButtonUp(UINT nFlags, CPoint point) 
{
	CTreeCtrl::OnLButtonUp(nFlags, point);

	if (m_bLDragging)
	{
		m_bLDragging = FALSE;
		CImageList::DragLeave(this);
		CImageList::EndDrag();
		ReleaseCapture();

		delete m_pDragImage;

		// Удаляем подсветку цели
		SelectDropTarget(NULL);

		if( m_hitemDrag == m_hitemDrop || m_hitemDrop == NULL)
			return;

		HTREEITEM htiParent = m_hitemDrop;
		while( (htiParent = GetParentItem( htiParent )) != NULL ) 
		{
			if( htiParent == m_hitemDrag ) return;
		}

		Expand( m_hitemDrop, TVE_EXPAND ) ;

		HTREEITEM htiNew = CopyBranch(m_hitemDrag, m_hitemDrop, TVI_LAST ); 
 		DeleteItem(m_hitemDrag);
		SelectItem( htiNew );
	}
}

 

Источник: ProtoSphere