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

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


Прокрутка во время 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