План
1. Події
миші.
2. Розпізнавання джерела події, натиснутих кнопок і
клавіш, координат курсору.
3. Події
клавіатури.
4. Перетягування даних між
об’єктами.
5. Перетягування
і вбудовування об'єктів — Drag&Doc.
Плаваючі вікна.
6. Буксування
компонентів у вікні програми.
1. Події миші
У компонентах C++Builder визначений
ряд подій, пов'язаних з мишею. Це події:
OnСlick - клацання мишею на компоненті і деякі інші
дії користувача.
OnDblClick - подвійне клацання миші на компоненті.
OnMouseDown - натиснення клавіші миші над компонентом.
Можливо розпізнавання натиснутої кнопки і координат курсору миші.
OnMouseMove - переміщення курсору миші над компонентом.
Можливо розпізнавання натиснутої кнопки і координат курсору миші.
OnMouseUp - відпускання раніше натиснутої кнопки миші над
компонентом. Можливо
розпізнавання натиснутої кнопки і координат курсору миші.
OnStartDrag - початок, процесу «перетягування» об'єкту.
Можливо розпізнавання перетягнутого об'єкту.
OnDragOver - переміщення «перетягнутого» об'єкту над
компонентом. Можливо розпізнавання перетягнутого 6об'єкту і координат курсору
миші.
OnDragDrop - відпускання раніше натиснутої кнопки миші
після «перетягування» об'єкту. Можливо розпізнавання перетягнутого об'єкту і
координат курсору миші.
OnEndDrag - ще одна подія при відпусканні раніше
натиснутої кнопки миші після «перетягування» об'єкту. Можливо розпізнавання
перетягнутого об'єкту і координат курсору миші.
OnEnter – подія, що виникає у момент отримання
елементом фокусу в результаті маніпуляції мишею, натиснення клавіші табуляції
або програмної передачі фокусу.
OnExit - подія у момент втрати елементом фокусу в
результаті маніпуляції мишею, натиснення клавіші табуляції або програмної
передачі фокусу.
OnMouseWheel - подія при обертанні коліщатка миші в
будь-яку сторону. Починаючи з C++Builder
5.
OnMouseWheelUp - обертання
коліщатка миші вгору. Наступає, якщо обертання не оброблене по подією OnMouseWheel.
OnMouseWheelDown - обертання
коліщатка миші вниз. Наступає, якщо обертання не оброблене подією OnMouseWheel.
Як
видно з приведеної таблиці, ці події охоплюють всі можливі маніпуляції з мишею
і навіть дублюють багато з них. Найширше використовується подія OnClick. Звичайно вона наступає,
якщо користувач клацнув на компоненті, тобто натиснув і відпустив кнопку миші,
коли покажчик миші знаходився на компоненті. Але ця подія відбувається також і
при деяких інших діях користувача. Вона наступає, якщо:
■
Користувач вибрав елемент в
таблиці, дереві, списку, випадному списку, натиснувши клавішу із стрілкою.
■
Користувач натиснув клавішу пропуску, коли кнопка або
індикатор були у фокусі.
■
Користувач натиснув клавішу Enter, а активна форма має кнопку за
умовчанням, вказану властивістю Default.
■
Користувач натиснув клавішу Esc, а активна форма має кнопку
переривання, вказану властивістю Cancel.
■
Викликаний метод Click елементу меню..
Для форми подія OnClick наступає,
якщо користувач клацнув на порожньому місці форми або на недоступному
компоненті.
Подія OnMouseMove відбуваються
постійно в процесі переміщення курсору миші і навіть просто при його тремтінні,
неминучому, якщо користувач не знімає руки з миші. Це треба враховувати і
користуватися цією подією дуже обережно, оскільки вона, на відміну від інших,
відбувається багато разів. Кожна дія користувача, пов'язане з натисненням або
відпусканням кнопки миші приводить до серії послідовно наступаючих подій. У
обробниках подій OnMouseDown і OnMouseUp можна розпізнати, яка кнопка миші натиснута, і в
якій точці компоненту знаходиться в даний момент курсор миші.
2.
Розпізнавання джерела події, натиснутих кнопок і клавіш, координат курсору
Крім параметра Sender в
обробники подій OnMouseDown і OnMouseUp передаються
параметри, що дозволяють розпізнати натиснуту кнопку, натиснуті при цьому
допоміжні клавіші, а також визначити координати курсору миші. Заголовок
обробника події OnMouseDown або OnMouseUp може мати, наприклад, наступний
вигляд:
void __fastcall TForm1::Edit1MouseDown(TObject *Sender,
TMouseButton Button, TShiftState Shift, int X, int Y)
Крім вже розглянутого нами параметра Sender в
обробник передаються параметри Button, Shift, X і
Y.
Параметр Button типу
TMouseButton
визначає натиснуту у цей момент
кнопку миші. Тип TMouseButton — тип, що перераховує, визначуваний таким чином:
Клавіша
|
Десяткове число
|
Шістнадцятиркове число
|
Символічнеім'я
|
F1
|
112
|
0x70
|
VK_F1
|
Enter
|
13
|
0x0D
|
VK_RETURN
|
Shift
|
16
|
0x10
|
VK_SHIFT
|
Ctrl
|
17
|
0x11
|
VK_CONTROL
|
Ait
|
18
|
0x12
|
VK_MENU
|
Esc
|
27
|
0x1В
|
VK_ESCAPE
|
0)
|
48
|
0x30
|
|
1!
|
49
|
0x31
|
|
n, N, t, T
|
78
|
0x4E
|
|
y,Y, h, H
|
89
|
0x59
|
|
наступний вигляд:
enum
TMouseButton (mbLeft,
mbRight,
mbMiddle);
Значення mbLeft відповідає натисненню
лівої кнопки миші, значення mbRight — правої, а значення mbMiddle —
середньої. Наприклад, якщо ви хочете, щоб обробник реагував на натиснення
тільки лівої кнопки, ви можете його першим оператором написати:
if (Button != mbLeft) return;
Тоді, якщо значення Button не рівне mbLeft,
тобто натиснута не ліва кнопка, виконання обробника
урветься.
Параметр Shift типу TShiftState визначає,
які допоміжні клавіші на клавіатурі натиснуті у момент натиснення кнопки миші. Оскільки
Shift є
множиною, перевіряти наявність в ньому тих або інших елементів треба методом Contains.
Наприклад, якщо ви хочете прореагувати на подію, що полягає в натисненні лівої
кнопки миші при натиснутій клавіші Alt,
можна використовувати оператора:
if ((Button == mbLeft)&&(Shift.Contains(ssAlt)))
У всі події, пов'язані з мишею, передаються також
координати курсору X і Y. Ці параметри визначають координати
курсору в клієнтській області компоненту. Завдяки цьому можна забезпечити різну
реакцію залежно від того, в якій частині клієнтської області розташований
курсор. Наприклад:
void __fastcall
TForm1::FormMouseMove(TObject *Sender,
TShiftState Shift, int
X, int Y)
{
StatusBar1->Panels->Items[0]->Text="x="+IntToStr(X);
StatusBar1->Panels->Items[1]->Text="y="+IntToStr(Y);
}
void __fastcall
TForm1::Button1MouseMove(TObject *Sender,
TShiftState Shift, int X, int Y)
{
if
(Button1->Cursor==crCross)
Button1->Visible=False;
Button1->Visible=True;
}
Розглянемо тепер події OnMouseWheel,
OnMouseWheelUp
і OnMouseWheelDown. Ці
події пов'язані з обертанням коліщатка, яке є в сучасних варіантах миші.
Заголовок обробника події OnMouseWheel має вигляд:
void __fastcall TForml::FormMouseWheel(TObject *Sender, TShiftState Shift,
int WheelDelta, TPoint &
MousePos, bool &Handled)
Параметри Sender і Shift не
відрізняються від розглянутих раніше. Параметр WheelDelta показує,
скільки разів обернулося коліщатко. Це позитивне число при обертанні вгору і
негативне — при обертанні вниз. Параметр MousePos типу TPoint визначає позицію курсорe миші. А параметр, що передається по посиланню Handled указує,
чи завершена обробка події.
Якщо обробник події OnMouseWheel відсутній
або якщо в ньому задано
Handled =false, то,
залежно від напряму обертання коліщатка, наступає подія OnMouseWheelUp або
OnMouseWheelDown.
Багато віконних компонентів — багаторядкові вікна
редагування, списки і т.п. мають обробники обертань коліщатка за умовчанням. Це
відноситься до Memo, RichEdit, ListBox,
TrackBar
і багатьох інших. Правда, ці обробники за умовчанням
спрацьовуватимуть тільки в тому випадку, якщо в компонентах задані смуги
прокрутки (власне, це обробники не самих елементів, а смуг прокрутки).
Обробники за умовчанням забезпечують при обертанні
коліщатка зрушення вмісту у відповідному напрямі. Отже писати обробники подій,
пов'язаних з коліщатком, має сенс тільки у випадках, коли треба забезпечити
нестандартну реакцію компонентів.
Наприклад, якщо ви задасте для форми обробники:
void __fastcall TForm1::FormMouseWheelUp (TObject *Sender,
TShiftState Shift, TPoint &MousePos, bool
&Handled)
{
( ScaleBy(101,100);
}
void __fastcall TForm1::FormMouseWheelDown(TObject *Sender,
TShiftState Shift, TPoint &MousePos,
bool &Handled)
{
(ScaleBy(100,101);
}
то при обертанні коліщатка розміри всіх компонентів
форми плавно зменшуватимуться або збільшуватимуться.
3. Події
клавіатури
OnKeyDown - подія наступає при натисненні користувачем будь-якої
клавіші. Можна розпізнати натиснуті клавіші, включаючи функціональні, і кнопки
миші, але не можна розпізнати символ натиснутої клавіші.
OnKeyPress - подія наступає при натисненні користувачем клавіши
символу. Можна розпізнати тільки натиснуту клавішу символу, розрізнити символ у
верхньому і нижньому регістрі, розрізнити символи кирилиці і латинські, але не
можна розпізнати функціональні клавіші і кнопки.
OnKeyUp - подія наступає при відпусканні користувачем будь-якої
клавіші. Можна розпізнати натиснуті клавіші, включаючи функціональні, і кнопки
миші, але не можна розпізнати символ клавіші, що відпускається.
Крім того, при натисненні користувачем клавіші
табуляції фокус може перемикатися з елементу на елемент, що викликає події OnEnter і OnExit.
Слід зазначити, що подія OnKeyPress свідомо наступає, якщо натискається тільки клавіша
символу або клавіша символу при натиснутій клавіші Shift.
Якщо ж клавіша символу натискається одночасно з якоюсь із додаткових клавіш, то
подія OnKeyPress
може не наступити (відбудуться тільки події OnKeyDown при
натисненні і OnKeyUp при відпуску) або, якщо і наступить, то вкаже на
невірний символ. Наприклад, при натиснутій клавіші Alt подія OnKeyPress при натисненні символьної клавіші не наступає. А
при натиснутій клавіші Ctrl подія OnKeyPress при
натисненні символьної клавіші наступає, але символ не розпізнається.
Заголовок обробника події OnKeyDown може
мати, наступний вигляд:
void
__fastcall TForml::Edit1KeyDown (TObject *Sender,
WORD &Key, TShiftState Shift)
В обробнику події OnKeyDown множина
можливих елементів параметра Shift скорочена до ssShift (натиснута
клавіша Shift), ssAlt (натиснута
клавіша Alt) і ssCtrl (натиснута
клавіша Ctrl). Інформація про натиснуті кнопки
миші відсутня.
Основний параметр, якого не було раніше - це
параметр Key. Зверніть увагу, що він
передається по посиланню, тобто може змінюватися в обробнику події. Крім того,
зверніть увагу, що це ціле число, а не символ. Параметр Key визначає натиснуту у момент події клавішу
клавіатури. Для не алфавітно - цифрових клавіш використовуються віртуальні коди
API Windows. Нижче, приведені декілька значень,
відповідних найбільш поширеним клавішам.
Перевіряти
натиснуту клавішу можна, порівнюючи Key з цілим
десятковим кодом клавіші. Наприклад, реакцію на натиснення користувачем клавіші
Enter можна оформити оператором:
if (Key == 13) … ;
Можна
порівнювати Key із
шістнадцятковим еквівалентом коду. Наприклад, приведеного вище оператора можна записати
у вигляді:
if (Key == 0x0D)…;
Для
клавіш, яким не відповідають символи, також відповідають іменовані константи,
які полегшують написання програми, оскільки не вимагають пам'ятати числові коди
клавіш. Наприклад, приведений вище оператор можна записати у вигляді:
іf (Key == VK_RETURN) … ;
Тепер
перейдемо до розгляду події OnKeyPress. Заголовок
обробника цієї події має вигляд:
void __fastcall TForm1::EditlKeyPress(TObject *Sender,
char &Key)
У
цей обробник, як і в описані вище, передається параметр Key,
що визначає натиснуту клавішу символу. Але тип цього
параметра не ціле число, як в попередніх випадках, а char — символ. В даному випадку в обробник передається
не віртуальний код клавіші, а символ, по якому можна визначити, прописна це
буква, або рядкова, російська, або латинська. Тому описаних вище складнощів з
розпізнаванням символів не виникає.
Хай,
наприклад, ви поставили користувачу питання, на яке він повинен відповісти
символами "Д" або "д" (так), або символами "Н"
або "н" (ні). Тоді розпізнати позитивну відповідь в обробнику події OnKeyPress можна
оператором:
if ((kеу == 'Д') || (Key == 'д'))… ;
Приведений
оператор реагує тільки на позитивну відповідь користувача, не реагуючи на
негативну або помилкову відповідь. Реакцію на всі можливі відповіді забезпечує
структура множинного вибору switch:
switch (Key)
{
case 'Д': …;
case 'д': …;
break;
case 'Н': …;
case 'н': …;
break;
default: Beep();
}
Тут передбачена реакція на позитивну і негативну
відповідь, а також звуковий сигнал при помилковій відповіді.
Подивившись на приведений раніше заголовок
обробника, ви можете побачити, що параметр Key передається
по посиланню. Це дозволяє в обробнику змінювати Key,
змінюючи відповідно його стандартну обробку в компоненті, оскільки ваш обробник
події спрацьовує раніше стандартного обробника компоненту. Хай, наприклад, ви
хочете мати на формі вікно редагування Edit1, в якому
користувач повинен вводити тільки цілі числа без знаку. Ви можете забезпечити
безпомилкове введення, підміняючи всі неприпустимі символи нульовим за
допомогою, наприклад, наступного коду:
Set < char, ' 0', ' 9'> Dig;
Dig <<'0'<<'1'<<'2'<<'3'<<'4'<<'5'<<'6'<<'7'<<'8'<<' 9';
if (! Dig.Contains(Key))
{ Key
= 0;
Beep ( );
}
При натисненні користувачем будь - якої клавіші,
окрім клавіш з цифрами, символи підміняються нульовим символом і просто не
з'являються у вікні редагування, як ви можете переконатися, зробивши
застосування з цим простим прикладом. Функція Beep відтворює
при натисненні користувачем помилкової клавіші звуковий сигнал.
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.
Аналогічним чином напишіть загальний для всіх списків
приведений вище
5.
Перетягування і вбудовування об'єктів — Drag&Doc.
Плаваючі вікна
Починаючи з C++Builder4
реалізована технологія перетягання і вбудовування віконних об'єктів — Drag&Doc. Ви можете познайомиться з
результатами цієї технології, працюючи з Інтегрованим
Середовищем Розробки C++Builder.
Вона надає користувачу повну свободу в перебудові інтерфейсу. Окремі вікна
можуть об'єднуватися разом, створювати багатосторінкове вікно із закладками,
потім знову відокремлюватися і т.д.
Подивимося, як подібний підхід можна реалізувати у своїй програмі.
У деяких віконних компонентів введено властивість DockSite, яка за умовчанням дорівнює false, але якщо її встановити в true,
то компонент стає контейнером: приймачем, здатним
приймати інші компоненти — клієнти. Ще одна властивість приймача — UseDockManager. Якщо вона дорівнює true (це
прийнято за умовчанням), то процесом вбудовування клієнта автоматично управляє
диспетчер вбудовування, відповідний тому компоненту, який є приймачем. Якщо ж
задати UseDockManager
рівним false,
то вбудовування клієнтів «лягає на плечі» програміста.
У компонентах — потенційних клієнтах треба встановити
властивість DragKind в dkDock. Крім
того, якщо ви хочете, щоб процес перетягання починався автоматично, то, так
само, як і в технології Drag&Drop, треба
встановити властивість DragMode в dmAutomatic.
Якщо залишити значення DragMode рівним
dmManual за
умовчанням, то управління процесом Drag&Doc здійснюється так само, як
описувалося раніше для процесу Drag&Drop.
Подивимося, до чого все це може привести. Давайте
побудуємо тестове застосування, в якому здійснимо Drag&Doc, не написавши жодного рядка
коду.
Почніть новий проект. Перенесіть на форму 6 панелей,
розташувавши їх так, У панелі Panel1 встановите
властивість DockSite в true.
Ця панель зможе служити у вас приймачем. У панелях Panel2 — Panel6 встановіть
DragKind в
dkDock і DragMode в dmAutomatic.
Запустіть застосування. Спробуйте перетягувати мишею
панелі Panel2 — Panel6, ви побачите, що панель Panel1 може
приймати інші панелі, розміщуючи їх усередині себе, причому спосіб вбудовування
залежить від того, в якій частині приймача ви завершуєте буксирування клієнта.
Між розміщеними клієнтами є роздільники, які дозволяють вам переміщати
буксируванням межі між клієнтами, змінюючи їх відносні розміри. Ви можете також
бачити, що навіть незалежно від наявності або відсутності в застосуванні
приймача, при клацанні на компонентах-клієнтах вони перетворюються на
самостійні плаваючі, які можна переміщати, виходячи навіть за межі батьківської
форми, можна змінювати їх розміри, можна закривати їх, після чого відповідні
компоненти зникають.
Приймачем може бути і сама форма. Вкажіть для форми
вашого проекту DockSite рівним true і запустіть його знову.
Поведінка панелей дещо зміниться. При клацанні на компонентах - клієнтах вони
як і раніше перетворюються на плаваючі вікна, які можна переміщати, змінювати
їх розміри і т.п. Але при повторному клацанні вони знову стають звичайними
панелями, зберігаючи при цьому змінене місце положення і розміри. Пояснюється це
тим, що повторне клацання сприймається в цьому випадку як вбудовування клієнта
в приймач — форму.
Давайте побудуємо багато віконий редактор текстів, що
має сховище, в яке можна
поміщати документи на зберігання і потім витягувати з нього потрібні документи.
Почніть новий проект і виконайте наступні пункти.
-
назвіть форму (властивість Name) Fmain.
-
розмістіть на формі компонент MainMenu і задайте в ньому всього один пункт
— Новий, маючи на увазі під цим
створення нового документа.
-
помістіть на форму панель,
задайте її властивість Caption рівним Сховище документів і задайте Align
= alTop.
-
помістіть на форму компонент PageControl і
задайте Align = alClient, щоб
він займав всю площу вікна, окрім смуги, зайнятою панеллю Сховище документів Задайте в ньому властивість DockSite рівним
true.
Цей компонент служитиме приймачем документів.
-
додайте в проект ще одну форму,
виконавши команду Fiie/NewForm. Назвіть
її FDoc (властивість
Name).
Задайте її властивість Visible рівним true.
Встановіть її властивість DragKind рівним
dkDock,
а властивість DragMode рівним
dmAutomatic.
Ця форма служитиме клієнтом компоненту TPageControl на
формі Fmain.
-
помістіть на форму FDoc компонент Memo, задавши
для нього Align = alClient, щоб
він зайняв всю форму. Зітріть в ньому текст (властивість Lines).
-
виконайте команду Project/Options і перенесіть
форму FDoc
з вікна Autocreate forms у вікно Available forms, оскільки вона повинна
створюватися не автоматично, а при виборі користувачем розділу меню Новий.
-
збережіть проект, назвавши модуль
з першою формою Umain, а з другою —UDoc.
Ми створили необхідні нам форми. Залишилося
написати декілька операторів, щоб це все працювало.
-
виконайте для модуля Umain команду
File/Include Unit Hdr і
підключіть з її допомогою заголовний файл UDoc.h,
щоб можна було посилатися на модуль UDoc.
-
введіть глобальну змінну LDoc типу TList. Вона є списком, в якому
зберігатимуться покажчики на створювані користувачем форми документів: TList
* LDoc;
-
у обробник події OnCreate форми Fmain запишіть оператор:
LDoc = new TList(); Цей оператор створює список LDoc.
-
у обробник події OnDestroy форми
Fmain запишіть
оператор:
delete
Ldoc; Цей
оператор звільняє пам'ять при закритті програми.
Залишилося написати обробник клацання на розділі меню
Новий. Він може мати
TFDoc * New = new TFDoc(this);
LDoc->Add(New);
New->Caption
= "Документ " + IntToStr(LDoc->Count);
У цьому обробнику вводиться змінна New типу TFDoc, відповідного
типу форми FDoc. Перший оператор обробника
динамічно розміщує нову форму FDoc і привласнює покажчик на неї
змінною New.
Наступний оператор додає покажчик на цю форму в список
LDoc.
А останній оператор задає заголовок вікна форми FDoc -
«Документ ...», де багатокрапка замінюється на номер, відповідний числу рядків
в списку LDoc.
Тепер ваша програма повністю готова. Збережіть її і
спробуйте в роботі. При клацанні на Новий
створюється нова форма документа, в якому ви можете писати текст. Документи
можна поміщати в сховище, в якому кожному документу автоматично відводиться
нова сторінка. Ви можете працювати з документом безпосередньо в сховищі, а
можете вилучити його звідти, перетворивши знову на окреме вікно.
6.
Буксування компонентів у вікні програми
У ряді випадків в Windows - програмах використовується
переміщення окремих компонентів в полі вікна або якоїсь панелі. Це переміщення
може бути переміщенням в якісь певні позиції (це легко здійснюється завданням
відповідних значень властивостям Left і Тор) або здійснюватися безперервним
буксируванням компоненту за допомогою миші. Простий приклад цього —
буксирування компонентів на площині форми в середовищі розробки C++Builder.
Подібні завдання виникають достатньо часто в технічних програмах, пов'язаних з
компоновкою якогось пристрою, з формуванням якихось схем (наприклад,
електричних) і т.п.
Випробувати різні способи буксирування можна в наступному
тестовому застосуванні. У ньому на формі розміщено чотири компоненти Image,
в які завантажені якісь зображення або частини
зображення. Якщо хочете відтворити приклад, в якому на окремих компонентах, як
на дитячих кубиках, відтворені фрагменти єдиного зображення, то можете в
обробник події форми OnCreate вставити наступний код:
TImage * Pict = new
TImage(Form1);
Pict->AutoSize = true;
// В наступному
операторі замість ... треба вказати ім'я файлу
Pict->Picture->LoadFromFile(“...”);
Image1->Canvas->CopyRect(Image1->ClientRect, Pict->Canvas,
Rect(0, 0, Pict->Width/2, Pict->Height/2));
Image2->Canvas->CopyRect(Image2->ClientRect, Pict->Canvas,
Rect(Pict->Width/2, 0, Pict->Width, Pict->Height/2));
Image3->Canvas->CopyRect(Image3->ClientRect, Pict->Canvas,
Rect(0, Pict->Height/2, Pict->Width/2, Pict->Height));
Image4->Canvas->CopyRect(Image4->ClientRect,Pict->Canvas,
Rect(Pict->Width/2, Pict->Height/2, Pict->Width, Pict->Height));
delete Pict;
У компонентах Image властивість
AutoSize повинна
бути встановлена в true.
Почнемо розгляд на цьому прикладі прийомів
буксирування. Всі приведені далі обробники подій записані в загальному вигляді.
Отже ви можете застосовувати їх одночасно до всіх ваших компонентів Image, а можете до різних компонентів
застосувати різні методи, щоб легше було порівнювати них один з одним.
Всі методи використовують декілька глобальних змінних,
оголошення яких треба помістити в модулі поза якими - набудь процедурами:
int X0,
Y0;
bool
move = false;
Змінна move визначає режим буксирування. Вона
встановлюватиметься в true на
початку буксирування і скидатися в false в кінці. Тому по значенню move можна
буде розрізняти переміщення миші з буксируванням і без неї. Змінні Х0 і Y0 будуть
потрібно нам для запам'ятовування координат курсору миші.
Один з можливих варіантів рішення нашої задачі —
буксирування самого компоненту. Буксирування починається при натисненні лівої
кнопки миші на відповідному компоненті Image. Тому
початок визначається подією OnMouseDown, обробник
якої має вигляд:
void __fastcall TForm1::Image1MouseDown(TObject *Sender,
TMouseButton
Button,TShiftState Shift, int X, int Y)
{if
(Button != mbLeft) return;
X0=X;
Y0=Y;
move =
true;
((TControl
*)Sender)->BringToFront() ;
}
Спочатку в цій процедурі перевіряється, чи
натиснута саме ліва кнопка миші (чи рівний параметр Button значенню mbLeft,
що позначає ліву кнопку). Потім в змінних Х0 і Y0
запам'ятовуються координати миші X і Y у цей момент часу. Задається режим буксування —
змінна move встановлюється
в true.
Останній оператор містить метод BringToFront, для
якого подія — ((TControl *)Sender),
висуває на
передній план відповідний компонент. Це дозволить йому надалі переміщатися
поверх інших аналогічних компонентів. Даний оператор не обов'язковий, але якщо його не записати, то в процесі
буксирування переміщуваний компонент може опинитися під іншими компонентами.
П6ід час буксирування компоненту працює його обробник
події OnMouseMove, що має вигляд:
void
__fastcall TForm1::Image1MouseMove(TObject *Sender,
TShiftState Shift, int X, int Y)
{if (move)
{TImage * Im = (TImage *)Sender;
Im->SetBounds(Im->Left + X – X0, Im->Top
+ У – Y0,
Im->Width, Im->Height);}
}
Він змінює за допомогою методу SetBounds координати
лівого верхнього кута на величину зрушення курсору миші (X - Х0 для
координати X
і Y -
Y0
для координати Y). Тим самим підтримується постійне розташування точки
курсору в системі координат компоненту, тобто компонент зміщується услід за
курсором. Ширина Width і висота Height компоненту залишаються
незмінними.
Після закінчення буксирування, коли користувач
відпустить кнопку миші, наступить подія OnMouseUp.
Обробник цієї події повинен містити всього один оператор:
move = false;
який вказуює програмі на закінчення буксування.
Тоді при наступних подіях OnMouseMove їх обробник, приведений
раніше, перестане змінювати координати компоненту.
На закінчення відзначимо ще один спосіб буксування,
мабуть, найбільш універсальний і простій. Він може бути застосований до
будь-яких віконних компонентів, включаючи форми. Пошліться в подіях OnMouseMove всіх компонентів, які ви хочете
буксирувати, на наступний універсальний обробник:
const int SC_DRAGMOVE = 0xF012;
ReleaseCapture();
((TControl *)Sender)->Perform(WM_SYSCOMMAND, SC_DRAGMOVE, 0);
Цей обробник звільняє курсор миші і посилає методом
Perform джерелу події Sender повідомлення Windows,
відповідне буксируванню.
Можете посилатися на подібний обробник в подіях
панелей, кнопок, списків, вікон редагування. Всі подібні компоненти користувач
зможе буксирувати, перебудовувавши таким чином інтерфейс програми. Якщо ви
пошлетеся на подібний обробник в події форми, то користувач зможе переміщати її
по екрану, узявшись за будь - яку її точку, а не тільки за точку в заголовку
вікна, як це прийнято в звичайних вікнах Windows.
Контрольні питання:
1.
Які події миші пов’язані із її пересуванням, а які із
клацанням по кнопкам?
2.
Із якими діями користувача може бути пов’язана подія OnClick?
3.
Які події, пов’язані із мишею повертають координати
положення курсору?
4.
У чому полягає відмінність подій, пов’язаних із
клавіатурою?
5.
Яким чином можна перевіряти натиснену клавішу клавіатури?
6.
Які властивості визначають пересування даних між
компонентами?
7.
Як у програмі можна реалізувати плаваючі інструментальні
панелі?
8.
Яким універсальним способом можна реалізувати буксування
віконних компонентів?
Немає коментарів:
Дописати коментар