Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Visual C++/MFC/WTL > Drag&Drop между разными CTreeView видами


Автор: voov 17.7.2008, 18:05
У меня есть 2 вида унаследованые от CTreeView. Нужно итем из дерева одного вида перетянуть на итем дерева другого вида.

Перетаскивание итема в дереве одного вида сделал по статье МСДН Dragging a Tree-View Item. 
В OnTvnBegindrag вида делаю 
Код

    CImageList* pImageList = GetTreeCtrl().CreateDragImage(pNMTreeView->itemNew.hItem);
    pImageList->BeginDrag(0, CPoint(0, 0));
    pImageList->DragEnter(this, pNMTreeView->ptDrag);

А в OnMouseMove 
Код

    CImageList::DragMove(point);

Вместе с курсором двигается картинка сделаная по итему дерева. Но при выходе курсора за пределы окна вида картинка исчезает.

Как сделать, чтобы картинка не исчезала?

Автор: Earnest 18.7.2008, 10:04
Можно попробовать в DragEnter другое окно передать (общего родителя или главное окно). Это именно оно все блокирует...

Автор: voov 18.7.2008, 10:54
Да, получилось. Earnest, спасибо.
Верно говорят, что утро вечера мудренее. Вчера был в полном тупике, а сегодня и сам уже начал двигаться в этом направлении.
В OnTvnBegindrag владельцем картинки сделал фрейм. Ниже код.
Кстати, может есть способ попроще добраться до родительского фрейма? У меня в фрейме 2 сплиттера, потому приходится вызывать GetParent 3 раза.
Код

    CWnd* frame = this->GetParent()->GetParent()->GetParent();
    ((CChildFrame*)frame)->SetCapture();

    pImageList->BeginDrag(0, CPoint(0, 0));
    pImageList->DragEnter(frame, pNMTreeView->ptDrag);

Нужно было еще DragMove делать
Код

CImageList::DragMove(point);
 в OnMouseMove фрейма, а не вида.

Автор: Earnest 18.7.2008, 11:35
GetParentFrame попробуй

Автор: voov 18.7.2008, 11:47
Цитата(Earnest @  18.7.2008,  11:35 Найти цитируемый пост)
GetParentFrame попробуй 

точно, спасибо.

Еще один момент. Теперь картинка появляется чуть выше чем нужно. Нужно в BeginDrag задать смещение вместо нулевой точки:

pImageList->BeginDrag(0, CPoint(0, 0));

но никак не могу получить это смещение...

Автор: Earnest 18.7.2008, 12:03
Хм... у меня были проблемы с горизонтальным смещением... я делала так (кусок обработчика BEGINDRAG):
  
Код

 m_hDragItem = nmtv.itemNew.hItem;
   CPoint pt = nmtv.ptDrag;

   CRect rc;
   m_tvOwner.GetItemRect (m_hDragItem, rc, true);

   m_pDragImage = m_tvOwner.CreateDragImage (m_hDragItem);
   m_pDragImage->BeginDrag (0, CPoint (pt.x - rc.left, 0));
   m_pDragImage->DragEnter (&m_tvOwner, pt);

А вертикальное если и есть - то незаметное: пока как следует не отъедет мышь, картинка сливается

Добавлено через 2 минуты и 21 секунду
Кстати, если во время mousemove ты помечаешь dropTarget в дереве, не забудь обернуть это в DragLeave\DragEnter, иначе грязь будет

Автор: voov 18.7.2008, 14:32
Решил проблему со смещением smile. Это было смещение верхнего левого угла онка фрейма относительно верхнего левого угла окна вида. Код вот такой.
Код

    CWnd* frame = GetParentFrame();
    frame->GetWindowRect(&rect);
    ScreenToClient(&rect);
    pImageList->BeginDrag(0, CPoint(rect.left, rect.top));
    pImageList->DragEnter(frame, pNMTreeView->ptDrag);

Теперь картинка появляется в том же месте, что и раньше (когда владельцем картинки был сам вид)
Код

    pImageList->BeginDrag(0, CPoint(0, 0));
    pImageList->DragEnter(this, pNMTreeView->ptDrag);

Автор: voov 18.7.2008, 14:54
Цитата(Earnest @  18.7.2008,  12:03 Найти цитируемый пост)
Кстати, если во время mousemove ты помечаешь dropTarget в дереве, не забудь обернуть это в DragLeave\DragEnter, иначе грязь будет 

Не совсем понял. 
В mousemove я делаю GetTreeCtrl().SelectDropTarget(hItem) чтобы пометить итем как цель.
Какая грязь будет? Можно поподробнее про эти  DragLeave\DragEnter (есть еще OnDragOver и OnDrop). Где и зачем нужно их использовать и что в них должно быть?

Сейчас начинаю операцию перетаскивания по событию TVN_BEGINDRAG в виде, слежу за мышью в OnMouseMove родительского фрейма, а заканчиваю операцию по WM_LBUTTONUP в том же фрейме. Может нужно как-то иначе?

Автор: Earnest 21.7.2008, 09:11
Цитата(voov @  18.7.2008,  15:54 Найти цитируемый пост)
В mousemove я делаю GetTreeCtrl().SelectDropTarget(hItem) чтобы пометить итем как цель.

Я тоже. И если не делаю DragLeave\DragEnter - то возникает грязь: либо недорисованне выделение, либо лишнее... Не всегда, но бывает. Перед SelectDropTarget вызываешь DragLeave, а после DragEnter.

Цитата(voov @  18.7.2008,  15:54 Найти цитируемый пост)
Сейчас начинаю операцию перетаскивания по событию TVN_BEGINDRAG в виде, слежу за мышью в OnMouseMove родительского фрейма, а заканчиваю операцию по WM_LBUTTONUP в том же фрейме. Может нужно как-то иначе? 

Все правильно. Только почему во фрейме-то MOUSEMOVE и LBUTTONUP... впрочем, если так удобнее, то дело твое. 
Я предпочитаю все делать в одном месте. А всю информацию о перетаскивании засунуть в отдельный класс, типа CDragInfo, чтобы не мусорить в классе-дереве, у которого есть и другие нагрузки.
А, поняла - ты же между разными видами таскаешь. Только если виды одного типа, я бы все равно фрейм грузить не стала. Одно дело - использовать ссылку на него в DragEnter, а другое - засовывать ненужную ему функциональность. Ты же capture ставишь на дерево - так что маус-мув по-любому получишь.

Автор: voov 21.7.2008, 09:54
Код

    ((CChildFrame*)frame)->SetCapture();

Capture я ставлю на фрейм. И перетаскивание нужно делать между разными видами. Виды разных типов, к тому же нужно будет таскать и на вид без дерева (потом).
В фрейме 2 обработчика MOUSEMOVE и LBUTTONUP, которые выполняют необходимые действия по перетаскиванию картинки, по координатам курсора определяют над каким видом мышь и переадресуют дальнейшую обработку в соответствующий вид.

Цитата(Earnest @  21.7.2008,  09:11 Найти цитируемый пост)
И если не делаю DragLeave\DragEnter - то возникает грязь: либо недорисованне выделение, либо лишнее... 

Так ты о методах CImageList. А я то думал о переопределенных методах вида (OnDragLeave, OnDragOver  ...). Чтобы нормально отрисовывались выделеные итемы дерева я использую DragShowNolock(). Примерно так
Код

    CImageList::DragShowNolock(false);
    GetTreeCtrl().SelectDropTarget(hTarget);
    CImageList::DragShowNolock(true);

Автор: Earnest 21.7.2008, 15:32
Да, действительно, DragShowNoLock, возможно, даже лучше...

Насчет идеологии: представь, что таскать нужно между видами разных фреймов - кто тогда обрабатывать должен?
Есть, конечно, возможность прорубиться через OLE Drag & Drop, но мне там активно не понравилось... Если речь идет о своем приложении, где ты царь и бог, проще ручками, как мы и делаем.
Я, при перетаскивании между разными окнами (вообще не связанными, но в моем приложении) делала так:
1) определяем структуру CDragInfo, куда помещаем ссылку на окно-владельца (кто начал drag & drop), итем, указатель (или hWnd) окна, куда будем кидать (это определяется во время таскания), таймер (для авто-скроллинга во время таскания) и т.д. - все, что может понадобиться для задачи - скажем, тип таскаемого элемента, etc
В этот класс переношу весь код, нужный для реализации (установку capture, выбор цели и прочая) 
2) определяю указатель на этот объект во всех классах, которые могут начать перетаскивание своих элементов
3) определяю несколько сообщений: TVM_DRAGENTER, TVM_DRAGLEAVE, TVM_DROPITEM - чтобы минимизировать обмен между разными окнами и их знание друг о друге.
4) На BeginDrag окно-владелец создает объект dragInfo, на mouseMove, lbuttonup и по Escape вызывает соответствующие функции объекта (в 2 последних случаях - потом разрушает его).
5) В процессе обработки mouseMove объект dragInfo посылает окну, над которым находится курсор, сообщение DRAGENTER - типа, хочешь, тебе чего-нибудь кинут? Если оно отвечает "хочу" - это будет новая цель, а старой, если она была, посылают DRAGLEAVE (чтобы она сняла выделение droptarget). DROPITEM посылается при завершении перетаскивания.
Таким образом, окна друг о друге могут ничего не знать, вся коммуникация идет через windows-сообщения + структура данных.
Вся функциональность (почти) - в классе DragInfo, окна-владельцы только вызывают в нужные моменты соответствующие функции + выделяют у себя цель + реализую вставку (и\или удаление).
Фактически, примерно так же работает OLE Drag & Drop, только там все гораздо тяжелее и накрученнее...

Автор: voov 21.7.2008, 16:23
Чтож, спасибо за информацию. Приму к сведению. 
Для текущей задачи достаточно того, что уже есть. К тому же, не хочу переписывать уже рабочий код. Но в будущем пригодится.  smile 

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)