БУКСИРОВКА ЭКРАННЫХ ОБЪЕКТОВ

Буксировка объектов пользовательского интерфейса (например, значков, панелей, элементов списка и др.) — один из самых эффектных приёмов, применяемых при работе с Windows-приложениями. Используя технику буксировки, вы можете “захватить” мышью графический объект (например, значок) и, удерживая кнопку мыши нажатой “перетянуть” его в другое место или окно. Чтобы “оставить” объект на новом месте, следует отпустить кнопку мыши. Вы, без сомнения, пользовались этими нехитрыми приемами много раз, например при перетягивании файлов из одной папки в другую Буксировка делает пользовательский интерфейс намного удобнее, поэтому хорошие приложения должны ее поддерживать.

ЧТО И КУДА БУКСИРОВАТЬ.

Прежде всего давайте решим, что и куда мы будем буксировать. В Delphi приложения состоят из компонентов, поэтому буксируемые объекты — это компоненты или их данные (например, элементы списка). Данные можно буксировать между компонентами и между формами. Мы изучим технику буксировки на примере перетягивания элементов из одного списка в другой .

Шаг 1. Начните в Delphi новое приложение. Затем поместите на форму два компонента ListBox и откорректируйте их положения и размеры. С помощью компонентов Label сделайте подписи к спискам. Над левым списком (компонент ListBoxl) напишите Available items, а над правым (компонент ListBox2) — Accepted items.

Шаг 2. Наполните элементами список с надписью Available items. Для этого активизируйте в Инспекторе Объектов свойство Items и щелкните мышью на кнопке с многоточием. На экране появится окно Редактора Строк (String list editor). Введите произвольный текст элементов (например, названия различных фруктов) и завершите ввод щелчком по кнопке ОК. В результате форма станет выглядеть так, как показано ниже на рисунке .

Организуем теперь буксировку элементов из списка с надписью Available items в список с надписью Accepted items.

С программной точки зрения операция буксировки — это цепочка событий, инициируемых пользователем, а обрабатываемых формой и ее компонентами. В этой цепочке выделяют следующие логические участки:

- начало буксировки;

- буксировка объекта над компонентами;

- оставление объекта на целевом компоненте;

- завершение буксировки.

НАЧАЛО БУКСИРОВКИ

Буксировка начинается с того, что пользователь нажимает левую кнопку мыши на визуальном компоненте и, удерживая ее нажатой, начинает перемещать мышь. Визуальные компоненты умеют автоматически обнаруживать эту ситуацию и запускать операцию буксировки. Для этого надо установить в компоненте свойство DragMode в значение dmAutomatic. По умолчанию DragMode имеет значение dmManual — буксировка должна обеспечиваться “вручную”, т. е. в обработчике события мыши OnMouseDown. Режим dmManual требует чуть больше программирования (по сравнению с режимом dmAutomatic), но позволяет сделать различные проверки, прежде чем решить, нужно ли начинать буксировку. В частности, обработчик события OnMouseDown может проверить, что нажата именно левая кнопка мыши и что курсор не попадает в пустую область списка, т. е. находится над некоторым элементом. По этой причине мы воспользуемся режимом dmManual.

Шаг 3. Определите для компонента ListBoxl следующий обработчик события OnMouseDown:

procedure TFormI. ListBox1MouseDown (Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);

begin

if Button = mbLeft then

if ListBoxl. ItemAtPos (Point (X, Y), True) >= 0 then ListBoxl. BeginDrag (False);

end;

Поясним работу метода. В параметрах обработки события передается информация о том, какая кнопка мыши была нажата (Button), состояние клавиш Shift, Alt, Ctrl (Shift), а также координаты курсора (X и Y) на момент нажатия кнопки. Буксировка начинается только при нажатии левой кнопки мыши (Button = mbLeft) и только в том случае, если курсор расположен над элементом списка (индекс элемента, возвращенный методом ItemAtPos, больше либо равен 0). Если эти два условия соблюдены, у компонента ListBox вызывается метод BeginDrag, который запускает операцию буксировки. Значение False, передаваемое в метод BeginDrag, указывает на то, что буксировка не должна начинаться, пока пользователь не сдвинет мышь на некоторое расстояние (по умолчанию 5 пикселов).

После начала буксировки в компоненте-источнике ListBoxl возникает событие OnStartDrag, которое позволяет выполнить любые подготовительные действия. В нашем примере нет необходимости его использовать.

Уже сейчас вы можете скомпилировать и запустить приложение, а затем попытаться буксировать элементы списка. Вы увидите, что начало буксировки сопровождается изменением курсора мыши, который превращается в перечеркнутый круг .

Курсор имеет такой вид тогда, когда он находится над формой и компонентами, которые не могут принимать буксируемые объекты (вы не можете оставить элемент списка где попало). К их числу относится список-источник с надписью Available items. Однако список-назначение Accepted items пока тоже никак не откликается на буксировку. Сейчас мы решим эту проблему.

БУКСИРОВКА ОБЪЕКТА НАД КОМПОНЕНТАМИ

Выбирая подходящее место назначения для объекта, пользователь перемещает мышь с нажатой кнопкой. Во время этого процесса программа не сидит сложа руки, а генерирует в каждом компоненте, над которым оказывается курсор мыши, событие OnDragOver. Обработчик этого события решает, примет ли компонент отбуксированный объект, если пользователь его оставит. Если компонент готов принять объект, обработчик должен возвратить True в булевском параметре Accept, который имеет событие OnDragOver.

Шаг 4. В нашем примере компонент ListBox2 должен принимать любые элементы списка, перетянутые из компонента ListBoxl, поэтому определите для компонента ListBox2 следующий обработчик события OnDragOver.

procedure TFormI. ListBox2DragOver (Sender, Source:Tobject; X,Y: Integer; State: TDragState; var Accept: Boolean);

begin

Accept: = Source= ListBoxl;

end;

В метод обработки события OnDragOver передается информация о том, какой компонент начал буксировку (параметр Source), текущие координаты курсора мыши (параметры Х и Y); параметр State позволяет узнать, движется курсор мыши внутрь компонента или наружу. В нашем случае текст обработчика лаконичен - элементы списка будут приниматься только из списка ListBox1.

Когда список Accepted items сигнализирует о своей готовности принять элемент списка, перетягиваемый из списка Available items, курсор мыши превращается из перечеркнутого круга в стрелку, к которой прикреплен листок бумаги .

Если вам не нравится этот вид курсора, то его можно изменить. Для этого активизируйте в Инспекторе Объектов компонент ListBoxl и установите новое значение для свойства DragCursor (по умолчанию DragCursor = crDrag).

ОСТАВЛЕНИЕ ОБЪЕКТА НА ЦЕЛЕВОМ КОМПОНЕНТЕ.

После того, как объект оказывается над целевым компонентом, пользователь все-таки решается его оставить и отпускает кнопку мыши. В этот момент компонент, в котором был оставлен буксируемый объект, получает событие OnDragDrop. Обработчик этого события играет ключевую роль во всей операции буксировки, выполняя с объектом необходимые действия.

Шаг 5. В нашем примере обработчик события OnDragDrop должен копировать элемент списка, выбранный из списка Available items в список Accepted items, т. е. из компонента ListBoxl в компонент ListBox2:

procedure TFormI. ListBox2DragDrop (Sender, Source: TObject; X, Y: Integer);

begin

with ListBoxl do ListBox2.Items.Add (Items [ItemIndex]);

end.

ЗАВЕРШЕНИЕ БУКСИРОВКИ

Сигнал о завершении буксировки получает не только тот компонент, в котором был оставлен объект, но и тот, из которого объект был взят. Для этого компоненту-источнику посылается событие OnEndDrag. Причем в параметре Target обработчику этого события передается информация о целевом компоненте.

Шаг 6. Давайте воспользуемся событием OnEndDrag для удаления элемента из списка Available items после его буксировки в список Accepted items (с точки зрения пользователя элементы будут не копироваться, а перемещаться из одного списка в другой). Определите для компонента ListBoxl следующий обработчик события OnEndDrag

procedure Tform1.ListBox1EndDrag(Sender, Target:TObject; X,Y: Integer);

begin

if Target = ListBox2 then

with ListBox1 do Items.Delete (Itemlndex);

end;

Шаг 7. Выполните компиляцию проекта и запустите приложение. Проверьте работу механизма буксировки.