Прокрутка во время drag'n'drop
Когда мы перетаскиваем элемент вполне возможна
ситуация, когда цель перетаскивания не видна.
Иногда пользователь разворачивает ветви так, что
без прокрутки не обойдешся. "Умные"
приложения обходят этот барьер.
Шаг 1: Добавление членов-переменных в класс
Мы объявляем две переменные. Одна описывает
таймер, другая счетчик тиков для таймера
protected:
UINT m_nTimerID;
UINT m_timerticks;
Шаг 2: Установка таймера в обработчике TVN_BEGINDRAG
Поместите следующий код в конец Вашего
обработчика (обычно это OnBeginDrag() ):
// Устанавливаем таймер
m_nTimerID = SetTimer(1, 75, NULL);
Шаг 3: Установка обработчика WM_TIMER
По умолчанию имя функции OnTimer(). Наперво убедимся,
что обрабатываем правильное сообщение таймера.
Затем обновляем расположение перетаскиваемого
изображения. И тогда определяем нужна ли
прокрутка. Если курсор cверху / снизу края или
близко к краю, то вычиляем скорость прокрутки.
Она изменяется в зависимости от удалености
курсора от края элемента управления и делится на
6 уровней.
void CTreeCtrlX::OnTimer(UINT nIDEvent)
{
if( nIDEvent != m_nTimerID )
{
CTreeCtrl::OnTimer(nIDEvent);
return;
}
// Не имеет значения то, что мы не инициализировали m_timerticks
m_timerticks++;
POINT pt;
GetCursorPos( &pt );
RECT rect;
GetClientRect( &rect );
ClientToScreen( &rect );
// Внимание: Экранные координаты используются потому что
// DragEnter использует окно Рабочего Стола
CImageList::DragMove(pt);
HTREEITEM hitem = GetFirstVisibleItem();
if( pt.y < rect.top + 10 )
{
// Мы должны прокручивать
// Делаем это медленно, если курсор около границы
int slowscroll = 6 - (rect.top + 10 - pt.y) / 20;
if( 0 == ( m_timerticks % (slowscroll > 0? slowscroll : 1) ) )
{
CImageList::DragShowNolock(FALSE);
SendMessage( WM_VSCROLL, SB_LINEUP);
SelectDropTarget(hitem);
m_hitemDrop = hitem;
CImageList::DragShowNolock(TRUE);
}
}
else if( pt.y > rect.bottom - 10 )
{
int slowscroll = 6 - (pt.y - rect.bottom + 10 ) / 20;
if( 0 == ( m_timerticks % (slowscroll >0? slowscroll : 1) ) )
{
CImageList::DragShowNolock(FALSE);
SendMessage( WM_VSCROLL, SB_LINEDOWN);
int nCount = GetVisibleCount();
for ( int i=0; i<nCount; ++i)
hitem = GetNextVisibleItem(hitem);
if( hitem )
SelectDropTarget(hitem);
m_hitemDrop = hitem;
CImageList::DragShowNolock(TRUE);
}
}
}
Шаг 4: Удаляем таймер, когда операция закончена
Вызовите KillTimer( m_nTimerID ), когда операция будет
закончена. Потребуется включить этот код в PreTranslateMessage(),
если Вы отменяете перетаскивание с помощью
клавиши Esc.
Источник: ProtoSphere
|