Главная » C++, C++ Builder » Реализация drag-and-drop в списках C++ Builder

0

CBuilder построен на таких вещах, которые нужны программисту постоянно. Система CBuilder не поддерживает напрямую drag-and-drop между двумя списками, но тем не менее в ней есть встроенная принципиальная возможность поддержки drag-and-drop.

Замечание

Здесь стоит отметить, что на самом деле люди имеют ввиду две вещи, когда говорят о drag-and- drop. Первая — это система, о которой мы говорим, перемещение данных между двумя компонентами, двумя формами или двумя объектами в системе. Вторая — перетаскивание файлов в окно программы. Эта форма drag-and-drop, обычно перетаскивание файлов из Windows Explorer в приложение, работающее под Windows 95 или NT, является отдельной статьей и будет рассмотрена в книге позднее, когда мы будем говорить о Windows API.

Итак, что именно нам нужно сделать для того, чтобы drag-and-drop работал в CBuilder? Не так уж и много на самом деле. Во-первых, давайте создадим форму для демонстрации перетаскивания элементов между двумя списками. Форма, используемая нами в этом примере, показана на рис.

4.8. На ней находятся два списка и метка, которую мы будем использовать для отображения состояния текущего процесса.

Рис. 4.8. Форма примера списков с drag-and-drop

Для того, чтобы поддерживать drag-and-drop в вашей форме, вам нужно обрабатывать два события для объектов, в которых вы хотите разрешить перетаскивание. Первое из них — событие OnDragDrop, которое происходит, когда пользователь тащит что-нибудь с  вашего  объекта или, опять же, роняет что-нибудь на ваш объект. Второе событие, которое вам нужно обработать — OnDragOver. Его обработчик вызывается для проверки корректности перетаскивания данных на ваш объект. Если не обрабатывать это событие, то курсор мыши превратиться в знак запрещения (перечеркнутый круг), когда он будет над вашим объектом.

Замечание

Исходный код для программы «Списки с Drag-And-Drop» находится на сопроводительном компакт-диске в каталоге Chapter4\DragDropList1.

В этом примере мы обработаем оба события OnDragOver и OnDragDrop для обоих списков.

Для начала давайте добавим обработчик события OnDragOver для обоих списков. Выберите оба списка на форма и переместитесь на страницу Events в Object Inspector. Напротив события OnDragOver наберите в поле имя метода OnDragOver. Так вы присвоите обработчик события сразу для двух объектов, что немного сэкономит вам время, а также продлит жизнь вашей мыши и клавиатуре. Введите следующий код в метод OnDragOver для обоих списков:

void __fastcall TForm1::OnDragOver(TObject *Sender, TObject *Source, int X, int Y,

TDragState State, bool &Accept)

{

Accept = true;

}

Этот метод просто сообщает, что оба списка способны воспринимать перетаскивание. Мы будем принимать перетаскиваемые данные, так как нам не очень важно, откуда они взялись  (будем считать, что с соседнего списка). Возвращаемый параметр Accept, который показывает, будет ли (true) объект позволять перетаскивать на него данные или нет (false) — это единственный параметр метода, значимый для CBuilder. Следующий метод, который нам нужен — метод DragDrop. Этот метод будет вызываться, когда пользователь притащит что-нибудь на наш список и отпустит кнопку мыши. Добавьте обработчик (для обоих списков) события OnDragDrop через Object Inspector. Назовите новый метод OnDragDrop и добавьте в него следующий код (общий для обоих списков):

void __fastcall TForm1::OnDragDrop(TObject *Sender, TObject *Source, int X, int Y)

{

TListBox *pList1 = (TListBox *)Source; TListBox *pList2 = (TListBox *)Sender;

// Нужно скопировать то, что выделено for ( int i=0; i<pList1->Items->Count; ++i ) if ( plist1->Selected[ i ] )

{

// Добавить его во второй список

pList2->Items->Add( pList1->Items->Strings[ i ] );

// И удалить из первого

pList1->Items->Delete( i );

}

}

Есть пара вещей, на которые стоит обратить внимание. Во-первых, не важно из какого списка в какой вы перетаскиваете. Код обработает оба случая. Причина этого в том, что CBuilder передает вам два параметра, указывающих откуда идет перетаскивание и куда. Объект Sender — объект, на который элементы роняются. Объект Source — объект, которому принадлежат перетаскиваемые данные.

Может быть, сразу это не совсем понятно. Однако, если подумать, аргумент Sender во всех обработчиках событий есть всегда объект, для которого было сгенерировано событие. В нашем случае событие состоит в том, что на объект притащили элементы. С аргументом Source (источник) яснее; это источник перетаскиваемой информации. Интерпретировать эту информацию и решать, что с ней делать, является нашей задачей.

Следующая интересная вещь в примере — поддержка множественного перетаскивания. Если вы перетащите три элемента из левого списка в правый, то эти три  элемента  исчезнут  из  левого списка и появятся в правом. Это уже, вероятно, превышает ваши ожидания. Кстати, чтобы это заработало, нужно установить свойство MultiSelect (множественный выбор) обоих списков в true (истина).

Третья и последняя интересная вещь в примере такова, что вам, вероятно, сразу не видна. Это пример сработает в случае, когда источник и получатель операции drag-and-drop — одно и то же. Если вы выберите элемент в первом списке (с левой стороны) и перетащите его на тот же (левый) список, то элемент пропадет из текущей позиции и появится в конце списка. Чего он не сделает, это он не появится там, куда вы переместили мышь. Мы разберемся с этим через пару минут.

Единственный способ протестировать пример — иметь какие-нибудь данные в списках изначально. Добавьте обработчик события формы OnCreate. Дайте новому обработчику имя FormCreate и добавьте в метод FormCreate следующий код:

void __fastcall TFOrm1::FormCreate(TObject *Sender)

{

ListBox1->Items->Add( "Элемент 1" ); ListBox1->Items->Add( "Элемент 2" ); ListBox1->Items->Add( "Элемент 3" ); ListBox1->Items->Add( "Элемент 4" ); ListBox1->Items->Add( "Элемент 5" );

}

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

Как узнать, когда начало?

Drag-and-drop редко бывает таким простым, как приведено выше.  Частенько  вам  нужно  будет знать, когда начинается процесс перетаскивания, чтобы вы могли установить какой-нибудь флаг или проверить, корректны ли данные для копирования. Пример — список с взаимно несовместимыми данными. Вы захотите запретить пользователю выбирать два элемента  и добавлять их к списку выбранных, если эти элементы не могут работать вместе. Вам нужно будет в начале перетаскивания данных удалять из них некорректные.

Конечно, CBuilder предоставляет возможности для этого.  Стали  бы  мы об  этом  разговаривать, если бы это было не так? Конечно, нет. В этом случае, однако, нужным обработчиком является обработчик события OnStartDrag. Когда мы создавали форму, я попросил вас добавить метку в нижней части формы. Давайте ее используем для отображения состояния того, что происходит.

Добавьте обработчик события StartDrag для первого объекта «список» (list box) в форме. Назовите обработчик ListBox1StartDrag. Добавьте следующий код в метод ListBox1StartDrag:

void __fastcall TForm1::ListBox1StartDrag(TObject *Sender, TDragObject  *&DragObject)

{

Label1->Caption = "Начало перетаскивания…";

}

Ничего особенного в этом обработчике нет. Мы всего лишь устанавливаем заголовок (или текст, caption) метки, отражая начало перетаскивания из списка. Точно также мы можем добавить обработчик события OnEndDrag, которое происходит при завершении операции перетаскивания. Создайте обработчик события OnEndDrag для первого списка и  назовите его ListBox1EndDrag. Добавьте следующий код для формы в метод ListBox1EndDrag:

void __fastcall TForm1::ListBox1EndDrag(TObject *Sender, TObject *Target, int X, int Y)

{

Label1->Caption = "Завершение перетаскивания…";

}

Скомпилируйте и запустите приложение. Выбирая элементы в списке, следите за текстом метки в нижней части формы. Заметьте, что просто выбор элемента и его перемещение начинает процесс перетаскивания. Процесс заканчивается, когда кнопка мыши где-либо отпускается. Если даже курсор мыши находится над тем же списком, вызывается событие OnEndDrag.

Теперь вы знаете достаточно о drag-and-drop в системе CBuilder, чтобы быть опасным. Пример drag-and-drop в списках показал вам, как делать некоторые вещи, но не до конца. Нужно сделать еще парочку улучшений.

Источник: Теллес М. – Borland C++ Builder. Библиотека программиста – 1998

По теме:

  • Комментарии