
- •Лекція №14
- •Тема 4. Розробка графічного інтерфейсу користувача (продовження) Обробка подій миші і клавіатури План
- •2. Розпізнавання джерела події, натиснутих кнопок і клавіш, координат курсору
- •3. Події клавіатури
- •4. Перетягування даних між об’єктами
- •5. Перетягування і вбудовування об'єктів — Drag&Doc. Плаваючі вікна
- •6. Буксування компонентів у вікні програми
4. Перетягування даних між об’єктами
Процес перетягування інформації за допомогою миші з одного об'єкту в іншій (Drag&Drop) дуже широко використовується у Windows. Наприклад, ви можете переміщати файли між теками, переміщати самі теки, включаючи їх в інші теки, і т.д. Всі властивості, методи і події, пов'язані з процесом перетягування, визначені в класі TControl, що є початковим батьківським класом для всіх візуальних компонентів C++Builder. Тому вони є загальними для всіх компонентів.
Початок процесу перетягування визначається властивістю DragMode, яка може встановлюватися в процесі проектування або програмно у значення dmManual або dmAutomatic. Значення dmAutomatic (автоматичне) визначає автоматичний початок процесу перетягання при натисненні користувачем кнопки миші над компонентом. Майте на увазі, що в цьому випадку подія OnMouseDown, пов'язана з натисненням користувачем кнопки миші, для цього компоненту взагалі не наступає. Значення ж dmManual (ручне) говорить про те, що почало процесу перетягання повинен визначати програміст. Для цього він повинен у відповідний момент викликати метод BeginDrag. Наприклад, він може помістити виклик цієї функції обробник події OnMouseDown, що наступає у момент натиснення кнопки миші. У цьому обробнику він може перевірити заздалегідь якісь умови (режим роботи програми, натиснення тих або інших кнопок миші і допоміжних клавіш) і при виконанні цих умов викликати BeginDrag.
Хай, наприклад, процес перетягання повинен початися, якщо користувач натиснув ліву кнопку миші і клавішу Alt над списком ListBox1. Тоді властивість DragMode цього компоненту треба встановити в dmManual, а його обробник події OnMouseDown може мати вигляд:
void __fastcall TForm1::ListBox1MouseDown (TObject *Sender,
TMouseButton Button, TShiftState Shift, int X, int Y)
{if ((Button == mbLeft) && Shift.Contains(ssAlt)
ListBox1->BeginDrag(false,5);
}
Параметр Button обробника події OnMouseDown показує, яка кнопка миші була натиснута, а параметр Shift є множиною, що містить позначення натиснутих у цей момент кнопок миші і допоміжних клавіш клавіатури. Приведений вище оператор перевіряє, чи натиснуті ліва кнопка миші і клавіша Alt. Якщо натиснуті, то викликається метод BeginDrag даного компоненту.
У попередньому прикладі у функцію BeginDrag передані значення параметрів false і 5. Перший з них означає, що процес перетягання почнеться не відразу, а тільки після того, як користувач зрушить мишу з натиснутою при цьому кнопкою на деяке число пікселів, а другий параметр указує величину переміщення — 5. Це дозволяє відрізнити просте клацання миші від початку перетягання. Якщо ж передати в BeginDrag значення true і будь-яке число, то перетягання почнеться негайно.
Коли почався процес перетягання, звичайний вид курсору змінюється. Поки він переміщається над формою або компонентами, які можуть прийняти інформацію, він звичайно має тип crNoDrop. Якщо ж він переміщається над компонентом, готовим прийняти інформацію, то набуває вигляду, визначуваного властивістю об'єкту DragCursor. За умовчанням ця властивість рівне crDrag. Треба підкреслити, що вид курсора визначається властивістю DragCursor перетягуваного об'єкту, а не того об'єкту, над яким переміщається курсор.
В процесі перетягання компоненти, над якими переміщається курсор, можуть повідомляти про готовність прийняти інформацію від перетягуваного об'єкту. Для цього в компоненті повинен бути передбачений обробник події OnDragOver, що наступає при переміщенні над даним компонентом курсору, що перетягує деякий об'єкт. У цьому обробнику треба перевірити, чи може даний компонент прийняти інформацію, і якщо не може, задати значення false в обробник параметру Accept. За умовчанням цей параметр рівний true, що означає можливість прийняти перетягуваний об'єкт. Обробник для списку ListBoxl може мати, наприклад, наступний вигляд:
Void __fastcall TForm1::ListBox1DragOver(TObject *Sender,
TObject *Source, int X, int Y, TDragState State, bool &Accept)
{ if (Sender ! = Source)
Accept = Source->ClassNameIs("TListBox");
else Accept = false;
}
У ньому спочатку перевіряється, чи не є даний компонент (Sender) і перетягуваний об'єкт (Source) одним і тим же об'єктом. Це зроблено, щоб уникнути перетягання інформації усередині одного і того ж списку. Якщо джерело і приймач є одним і тим же об'єктом, то спрацьовує else і параметр Accept стає рівним false, забороняючи прийом інформації. Якщо ж це різні об'єкти, то Accept робиться рівним true, якщо джерелом є якийсь інший список (компонент класу TListBox).
Процедура прийому інформації від об'єкту записується в обробнику події OnDragDrofp приймаючого компоненту. Ця подія наступає, якщо після перетягання користувач відпустив кнопку миші над даним компонентом. У обробник цієї події передаються параметри Source (об'єкт - джерело) і X і Y координати курсорe. Якщо продовжити початий вище приклад перетягання інформації з одного списку в іншій, то обробник події OnDragDrop може мати вигляд:
Void __fastcall TForm1::ListBox1DragDrop(TObject *Sender, TObject *Source,
int X, int Y)
{
TListBox *S = (TListBox *) Source;
((ListBox *)Sender)->Items->Add(S->Items->Strings[S->ItemIndex]);
}
У цьому обробнику перший оператор створює покажчик S на об'єкт класу ListBox, і передає в нього посилання на об'єкт Source, що сприймається як об'єкт TListBox. Це зроблено просто для того, щоб не повторювати кілька разів в наступному операторі приведення типу Source до покажчика на об'єкт класу TListBox. A таке приведення типу необхідне з наступної причини. Параметр Source оголошений як покажчик на об'єкт класу TObject. Але в цьому класі відсутні властивості Items і ItemIndex. Вони є тільки в класі TListBox. Тому перш, ніж звернутися до цих властивостей, треба провести відповідне приведення типу Source.
Другий оператор обробника заносить методом Add виділений рядок списку -джерела S в список-приймач Sender. У цьому перенесенні інформації з одного списку в іншій і полягає в нашому прикладі Drag&Drop.
У приведеному обробнику можна було б обійтися і без створення покажчика S. В цьому випадку обробник мав би наступний вигляд:
Void __fastcall TForm1::ListBoxlDragDrop(TObject *Sender, TObject *Source, int X, int Y)
{
((TListBox*)Sender)->Items->Add(((TListBox *)Source)->
Items->Strings[((TListBox *)Source)->ItemIndex]);
}
Після завершення або переривання перетягання наступає подія ОnEndDrag, в обробнику якого можна передбачити якісь додаткові дії. Є також пов'язана з перетяганням подія OnStartDrag, яка дозволяє провести якісь операції на початку перетягання. Ця подія корисно при автоматичному початку перетягання, коли іншим способом цей момент не можна зафіксувати.
Тепер, об'єднавши приведені вище фрагменти обробки перетягання, підсумуємо, що треба зробити, якщо ви маєте в програмі декілька списків ListBox і хочете забезпечити можливість копіювання рядків кожного з цих списків в будь-якій іншій.
Це потребує виконання двох операцій:
Виділіть мишею всі списки програми як одну групу, перейдіть в Інспекторі Об'єктів на сторінку подій, зробіть подвійне клацання біля рядка події OnDragOver і напишіть в Редакторі Коду приведений вище обробник події OnDragOver.
Аналогічним чином напишіть загальний для всіх списків приведений вище обробник події OnDragDrop.