Реализация drag'n'drop в CTreeView -- 2 метод
"Операция при которой пользователь
используем мышь или другое указывающее
устройство для перемещения данных из одного окна
в другое". Именно так звучит определение Drag'n'Drop
в MSDN. В данном примере мы рассмотрим метод именно
_перемещения_ данных пользователем. Все делается
в 5 шагов:
Шаг 1: Убедитесь, что Ваше дерево поддерживает
drag'n'drop
Флаг TVS_DISABLEDRAGDROP должен быть отключен. Иначе
сообщение TVN_BEGINDRAG не будет послано.
Шаг 2: Объявите переменные-члены
protected:
CImageList* m_pDragImage; //содержит список изображений используемый во время переноса
BOOL m_bLDragging;
HTREEITEM m_hitemDrag,m_hitemDrop;
Шаг 3: Добавьте обработчик для TVN_BEGINDRAG
Функция BeginDrag() вызывается с аргументами 0
и координатами точки. Ноль обозначает номер
изображения в списке, точка - координаты
относительно курсора этого изображения. Затем мы
вызываем DragEnter(), чтобы показать изображение.
Первый параметр NULL - для того, чтобы
изображение было видно за пределами дерева.
void CTreeCtrlX::OnBeginDrag(NMHDR* pNMHDR, LRESULT* pResult)
{
NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;
*pResult = 0;
m_hitemDrag = pNMTreeView->itemNew.hItem;
m_hitemDrop = NULL;
m_pDragImage = CreateDragImage(m_hitemDrag); // get the image list for dragging
// 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();
}
Шаг 4: Добавьте обработчик для WM_MOUSEMOVE -
обновление перетаскивамого изображения
В этом обработчике мы обновляем позицию
взятого изображения и цель бросания. Функция DragMove()
перемещает изображение. Затем мы должны обновить
цель, если мышь появляется над деревом. Делаем
это с помощью двух вызовов функции DragShowNolock().
Первый: скрывает изображение и позволяет
обновить информацию, второй: показывает
изображение снова. Можно было бы использовать DragLeave()
/ DragEnter(), но тут иногда возникают проблемы
перерисовки.
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);
SelectDropTarget(hitem);
m_hitemDrop = hitem;
CImageList::DragShowNolock(TRUE);
}
}
CTreeCtrl::OnMouseMove(nFlags, point);
}
Шаг 5: Наконец добавьте обработчик для WM_LBUTTONUP
В обработчике WM_LBUTTONUP мы завершаем drag'n'drop
процесс. Сначала мы определяем, что находимся в
режиме drag'n'grop. Это хорошее место для того, чтобы
удалить список изображений созданный функцией CreateDragImage()
в методе OnBeginDrag(). Перед перемещением
убедимся, что все действия кооректны. В вызове
Expand(), в принципе, нет необходимости, но если мы
объявили динамическую загрузку, где элементы
загружаются, когда ветвь раскрывается, он
необходим.
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 )
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
|