Рассылка закрыта
При закрытии подписчики были переданы в рассылку "Все о SEO. Раскрутка сайтов" на которую и рекомендуем вам подписаться.
Вы можете найти рассылки сходной тематики в Каталоге рассылок.
Август 2001 → | ||||||
1
|
2
|
3
|
4
|
5
|
||
---|---|---|---|---|---|---|
6
|
7
|
8
|
9
|
10
|
11
|
12
|
13
|
14
|
15
|
16
|
17
|
18
|
19
|
20
|
21
|
23
|
24
|
25
|
||
27
|
28
|
30
|
31
|
Статистика
0 за неделю
Borland C++ Builder всякая всячина (Drag & Drop)
Приветствую всех получателей рассылки Borland C++ Builder - всякая всячина!
"Есть только один интуитивно понятный интерфейс - соска, все остальное осваивается путем обучения..."
(Автор Неизвестный)
Сегодня мы рассмотрим такую интересеую особенность элементов управления, как Drag & Drop. В настоящее время любой более-менее серьезный программный продукт должен обладать хорошо развитым интерфейсом, простым в использовании, но в то же время - дающим возможность разным пользователям адаптировать его под свой стиль работы (перевожу: одни пользователи предпочитают большинство операций делать мышью, другие используют в основном клавиатуру). Нет и не может быть какого-то узаконенного предпочтения того или иного способа управления. Поэтому вы в своих программах просто обязаны предусмотреть управление как мышкой, так и с помощью горячих клавиш.
Метод Drag & Drop как раз и является одним из ключевых моментов управления программой с помощью мыши. "В лоб" это переводится как "перетащить и бросить" и обозначает захват левой кнопкой мыши какого-либо объекта формы (или его элемента) и перетаскивание его в другое место (на другой объект формы). С помощью этого метода реализуются: изменение порядка следования в списках, перенос элементов из одного списка в другой, перемещение элементов по форме и множество других производных операций.
Давайте немного подробнее рассмотрим некоторые из этих операций (если объяснение займет слишком много места, я вынесу объяснение некоторых свойств Drag & Drop в следующий выпуск нашей пассылки).
Чтобы не было путаницы, в дальнейшем я постараюсь именовать свойства так: OnDragOver и OnDragDrop, поскольку это все-таки полноценные свойства элементы, ссылающиеся на функцию-обработчика соответствующего события, а события, обрабатываемые этой функцией, буду называть соответственно DragOver и DragDrop.
Прежде всего, разберемся и инструментарием: создайте новый проект с формочкой Form1. Закиньте на нее два списка TListBox (ListBox1 и ListBox2) - я буду их иногда называть соответственно левый и правый списки. Установите для обоих списков свойство DragMode в dmAutomatic. С этого и начнем. Выберите на форме левый список, в Object Inspector перейдите к вкладке Events и сделайте двойной щелчек мышью на свойствах списка OnDragOver и OnDragDrop. Билдер сформирует два новых метода для главной формы: Form1::ListBox1DragOver и Form1::ListBox1DragDrop. Событие DragOver возникает, говоря человеческим языком, когда вы "тащите" что-либо через какой-нибудь элемент формы, причем это событие возникает не для того элемента, который вы "тащите", а для элемента, над которым в данный момент находится курсор мыша. То есть, для корректной работы механизма Drag & Drop вы должны определить свойство OnDragOver и для элемента-источника (Source) и для элемента-приемника (Dest или Destination). Все сказанное касается также и события DragDrop, которое активирует элемент формы, на который вы попытались "сбросить" элемент источник.
Исходя из этого, залезьте еще раз на вкладку Events в Object Inspector и, выделив на форме список ListBox1, переименуйте его свойства ListBox1DragOver и ListBox1DragDrop в ListBoxDragOver и ListBoxDragDrop, затем выделите список ListBox2 и на той же вкладке выберите для его свойств OnDragOver и OnDragDrop из выпадающего списка те же значения (ListBoxDragOver и ListBoxDragDrop). Это позволит нам для обоих списков использовать одни и те же функции-обработчики событий (к слову, это очень хорошо экономит время и нервы при разработке больших форм).
Еще одно замечание: поскольку все свойства, касающиеся метода Drag & Drop определены в классе TControl, то из этого следует, что метод Drag & Drop будут поддерживать ВСЕ видимые элементы формы, включая даже TLabel и TImage, не обладающие собственным фокусом ввода. Пусть ваша интуиция подскажет вам, как это можно использовать...
Возвращаемся к баранам: к этому моменту в модуле главной формы у вас уже должен быть автоматически сформирован следующий код:
И тот и другой метод получают несколько одинаковых параметров:
- TObject *Sender - это указатель на элемент, активировавший данное событие, в нашем случае это - список, над которым "протаскивается" в данный момент какой-нибудь объект (событие DragOver), или элемент, на который этот объект сбрасывается (событие DragDrop).
- TObject *Source - указатель на объект-источник, то есть объект, к которому и был применен метод Drag & Drop.
- int X и int Y - координаты курсора в пределах объекта Sender.
- TDragState State - принимающий одно из значений:
- dsDragEnter - курсор "начинает" движение над элементом Sender
- dsDragMove - курсор "продолжает" движение над элементом Sender
- dsDragLeave - курсор "заканчивает" движение над элементом Sender и переходит на другой элемент
- bool &Accept - изначально установлена в true, вы можете назначить ей значение false, если не желаете в силу каких-то условий "опускать" элемент Source на данную позицию элемента Sender. В этом случае курсор из листочка изменится на перечеркнутый круг, и , даже если пользователь отпустит в этот момент клавишу мыши, активизации события DragDrop не произойдет.
Теперь нарисуйте для метода ListBoxDragOver следующий код:
if (!(dynamic_cast<TListBox *>(Source) && dynamic_cast<TListBox *>(Sender))) Accept= false;
}
Прежде всего - об использовании оператора dynamic_cast. Как вы видели, большинство функций-обработчиков событий получают в качастве параметра ссылку на объект, инициировавший данное событие. Для унификации обработки эти ссылки даются на объект типа TObject, поскольку все классы VCL порождаются именно от этого класса. То есть, для разных по типу элементов формы мы можем назначить одну функцию-обработчик события, например, DragDrop. Однако, это порождает и некоторые проблемы при работе с такими объектами, а именно, используя указатель на TObject, мы имеем доступ только к его свойствам и методам. Для того, чтобы получить доступ к свойствам и методам его потомка и был введен оператор dynamic_cast. Он "пытается", используя так называемый RTTI (runtime type identification) класса, "привести" указазатель на объект к нужному типу. Если это невозможно, он возвращает NULL.
Рассмотрим теперь метод ListBoxDragOver. Единственный его оператор if пытается выяснить, можно ли привести указатели Source и Sender к типу TListBox или, другими словами, являются ли объекты Source и Sender объектами типа TListBox. Если хоть один из них не является объектом типа TListBox, то переменная Accept устанавливается как false и, таким образом, запрещается операция Drag & Drop над этими объектами. Откомпилируйте и запустите этот пример. Попробуйте "захватить" мышкой элемент (строку) одного из списков (на самом деле пока вы захватываете только лишь сам список) и "перетащить" его (ее) в любое место формы. Вы увидите, что курсор принимает форму перечеркнутого круга везде, кроме одного из списков TListBox. Причем созданный нами код допускает применение операции Drag & Drop и для списка-источника. Если вы не нуждаетесь в возможности изменения порядка следования элементов в списке и желаете сделать только перенос с помощью метода Drag & Drop элементов из одного списка в другой, модифицируйте слегка код метода ListBoxDragOver:
if (!(dynamic_cast<TListBox *>(Source) && dynamic_cast<TListBox *>(Sender)) || Source==Sender) Accept= false;
}
То есть, мы постановили, что если Source (объект, инициировавший операцию Drag & Drop) и Sender (объект, над которым проносится в данный момент курсор) - одно и то же "лицо", то операция Drag & Drop - запрещена.
Однако, вернемся к первому варианту. Добавьте в метод ListBoxDragDrop следующий код:
if (dynamic_cast<TListBox *>(Source) && dynamic_cast<TListBox *>(Sender))
{
}TListBox *Src= dynamic_cast<TListBox *>(Source);
TListBox *Dest= dynamic_cast<TListBox *>(Sender);
int Index= Dest->ItemAtPos(TPoint(X, Y), false);
if (Src->ItemIndex>=0)
{
}if (Src!=Dest)
{
if (Index>=0) Dest->Items->Insert(Index, Src->Items->Strings[Src->ItemIndex]);
Src->Items->Delete(Src->ItemIndex);
}else
{
if (Index>=0 && Index<Src->Items->Count) Src->Items->Move(Src->ItemIndex, Index);
else Src->Items->Move(Src->ItemIndex, Src->Items->Count - 1);
}Dest->ItemIndex= Index;
}
Во первых строках мы аналогичным образом проверяем, принадлежат ли объекты Source и Sender к типу TListBox, только сейчас объект Sender - не объект, над которым двигается курсор мыши, а объект, на который мы попытались "сбросить" Source, инициировавший операцию Drag & Drop. Далее мы, для удобства работы, объявляем два указателя Src и Dest на объект-источник и объект-приемник, но уже не типа TObject, а типа TListBox, чтобы получить полный доступ к его свойствам. Теперь нам надо определить - какие строки (Source) и куда (Sender) переносить. Для объекта Source это не представляет большого труда: "захватывая" объект мышью, мы нажимаем левую кнопку мыша, что приводит к выделению определенной строки в списке. Подразумевается, что именно эту строку мы и будем переносить. Поэтому мы можем для определения ее индекса воспользоваться свойством Src->ItemIndex, только для гарантии нужно убедиться, что строка действительно выделена (Src->ItemIndex не равно -1). Для объекта-приемника все чуть-чуть сложнее, поскольку формально мы в нем ничего не выделяем. Но у класса TListBox есть метод ItemAtPos, который по координатам X и Y выдает индекс "предполагаемого места падения". Второй аргумент этого метода определяет, как ему реагировать, если координаты не попадают ни на один элемент списка: true означает, что в данной ситуации будет возвращено значение -1, а false - что мы получим значение TListBox::Items->Count, то есть индекс предполагаемого элемента, следующего за последним существующим. В нашем случае удобно воспользоваться вторым вариантом, то есть, если будут какие-то сомнения относительно того, куда "уронить" строку, она будет помещена в конец списка.
Кроме этого мы должны отработать вышеописанную ситуцию, когда строка переносится вверх или вниз в пределах одного списка, то есть когда Src==Dest. В зависимости от результата этой проверки мы производим одно из следующих действий:
- Переносим строку из одного списка в другой с помощью методов Insert и Delete свойства Items списка (TStrings). Если индекс в списке-приемнике не попадает в диапазон существующих (от 0 до Count-1), то строка будет добавлена в конец списка.
- Переносим строку в пределах списка с помощью метода Move свойства Items объекта-источника. В этом случае мы должны отследить еще и ситуацию с некорректным индексом и "поправить" его, если он выходит за пределы возможного диапазона.
Попробуйте запустить этот пример, только предварительно в Object Inspector для одного из списков в его свойстве Items создайте несколько произвольных строк. Попробуйте перетащить строчки из одного списка в другой и обратно, а также поменять порядок следования строк в пределах одного списка. Есть еще одна особенность: создайте на форме еще пару списков (ListBox3 и ListBox4), в Object Inspector установите для них свойства DragMode в dmAutomatic, а свойства OnDragOver и OnDragDrop соответственно - в ListBoxDragOver и ListBoxDragDrop. Запустите обновленный пример: теперь вы можете производить операции Drag & Drop уже между четырьмя (!) списками. То есть, создав один раз функции-обработчики событий, вы можете использовать их для вновь создаваемых объектов.
На этой оптимистической ноте я хотел бы закончить текущий выпуск. В следующем выпуске мы продолжим тему метода Drag & Drop и рассмотрим более сложные способы манипулирования объектами на форме.
http://subscribe.ru/
E-mail: ask@subscribe.ru |
Отписаться
Убрать рекламу | Рейтингуется SpyLog |
В избранное | ||