Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Теллес М. - Borland C++ Builder. Библиотека программиста - 1998

.pdf
Скачиваний:
764
Добавлен:
13.08.2013
Размер:
4.35 Mб
Скачать

Borland C++ Builder (+CD). Библиотека программиста 71

}

Как видите, удаление управляющего элемента с формы это просто удаление указателя на объект VCL. Как только мы удалили объект, мы устанавливаем указатель в NULL, чтобы быть уверенными, что про этот указатель будет точно известно, что он пуст. Это хорошая привычка, ее стоит придерживаться в ваших программах на C++. Всегда ставьте указатель в NULL, как только вы закончили с ним работать.

Когда объект удален, мы снова ставим доступной первую кнопку, чтобы можно было создать новый объект, и ставим кнопки 2 и 3 недоступными, так как в данный момент объекта полоски прогресса не существует.

Последний штрих

Есть один последний шаг. Форма будет замечательно работать и не будет возникать никаких проблем. Однако если пользователь закроет форму, не удалив полоску прогресса, то часть памяти в системе будет потеряна. Чтобы этого избежать, мы добавляем деструктор класса формы. Добавьте следующее объявление в заголовочный файл:

private: // User declarations TProgressBar *FpProgress;

public: // User declarations

__fastcall TForm1(TComponent *Owner); __fastcall ~TForm1(void);

};

А затем добавьте код деструктора в исходный файл (Unit1.cpp): __fastcall TForm1::~TForm1(void)

{

if ( FpProgress ) delete FpProgress;

}

Заметьте: мы опять проверяем, что указатель не NULL. Попытка удалить указатель, равный NULL, может привести к сбою программы.

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

Далее мы рассмотрим, как проверять правильность ввода в управляющих элементах в CBuilder.

Проверка корректности данных в поле ввода

Одна из важных граней многих программ (особенно предназначенных для неопытных пользователей) — проверка того, что введенные данные корректны. Как только данные прошли через приложение, они находятся в постоянном хранилище, например в базе данных, независимо от того, корректны ли они и соответствуют ли нужному формату. Я не могу сказать, сколько раз у меня происходил сбой программы из-за того, что якобы правильные данные, которые она считывала из базы данных, хранились в неправильном формате. Поля, в которых не должно было быть ничего, кроме цифр, содержали буквы, а поля, гарантированно содержащие целые значения, содержали десятичную точку. Этот список можно продолжать долго.

Borland C++ Builder (+CD). Библиотека программиста 72

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

Есть два основных момента в проверке ввода. Первый это убедиться, что только разрешенные символы попадают во введенные данные. Для ввода числа, например, нет смысла разрешать пользователю вводить буквы. Если ввод представляет собой целое число, то нужно также запретить вводить десятичную точку.

Второй момент в проверке данных проверять, что введенное число лежит в нужном диапазоне. Например, если подразумевается ввод целого от единицы до десяти, то вы можете проверять, что пользователь вводит цифры и не вводит десятичную точку. Но это не поможет при вводе 12345678. Это число, скорее всего, породит проблемы при записи данных, когда будет ожидаться не более чем двузначное число.

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

Замечание

Исходный код для программы «Проверка ввода» находится в каталоге Chapter4\EditValidation на сопроводительном компакт-диске.

На рис. 4.7 приведена форма, которую мы будем использовать в этом примере. Чтобы ее получить, нужны два поля ввода (TEdit), три переключателя (TRadioButton) и два поля статического текста в тех позициях, как на рисунке. При помощи этого мы будем проверять ввод несколькими способами.

Рис. 4.7. Форма для примера проверки данных

Когда вы построите визуальную форму, добавьте обработчик выбора переключателя Только цифры. Если этот переключатель установлен, то компонент TEdit (поле ввода) над переключателем будем принимать только цифры (от 0 до 9, без десятичной точки) для ввода в

поле. Добавьту следующий код в обработчик:

Borland C++ Builder (+CD). Библиотека программиста 73

void __fastcall TForm1::RadioButton1Click(TObject *Sender)

{

Label1->Caption = "Вводите только цифры:"; Edit1->OnKeyPress = Edit1Keypress;

}

В приведенном примере мы устанавливаем текст в статическом поле в «Вводите только цифры: ». Это помогает пользователю разобраться, что к чему. После этого мы устанавливаем, что событие KeyPress (нажатие клавиши) поля ввода будет обрабатывать проверяющая процедура Edit1KeyPress. Далее следует код для процедуры Edit1KeyPress, которая будет вызываться при нажатии пользователем клавиши в поле ввода Edit1, если установлен первый переключатель:

void __fastcall TForm1::Edit1KeyPress(TObject *Sender, char &Key)

{

if ( !isdigit(Key) ) Key = 0;

}

Эта процедура просто проверяет каждый символ, вводимый пользователем. Функция isdigit, находящаяся в заголовочном файле ctype.h, который вам нужно подключить в начале исходного файла для формы, проверяет, лежит ли символ в диапазоне от 0 до 9. Если это так, то функция возвращает TRUE (истина). Иначе функция возвращает FALSE (ложь). Синтаксис !isdigit в C++ означает, что следующий код будет выполняться тогда и только тогда, когда функция вернет FALSE. Установка параметра Key в 0 говорит компоненту TEdit, что этот символ нужно игнорировать.

Скомпилируйте и запустите программу. Щелкните на первом переключателе, для которого мы только что добавляли обработчик и затем щелкните в поле ввода. Попробуйте ввести букву, например «а». Угадайте, что будет? Если вы решили, что символ не попадет в поле ввода, то вы ошиблись. Поле ввода радостно отобразит символ «а», тем самым руша стратегию проверки ввода.

Может, весь подход неправилен? Может, мы не можем проверять ввод посимвольно в CBuilder? Если это так, то это трагедия. К счастью, это не так. Проблема в том, что мы пропустили один важный шаг.

Закройте приложение и перейдите в дизайнер форм в CBuilder. Выберите форму, щелкнув где- либо в клиентской области формы, не содержащей компонентов. Или вы можете перейти в Object Inspector и выбрать объект Form1 из выпадающего списка в верхней части окна инспектора. В любом случае перейдите к свойству формы KeyPreview и установите его в TRUE (истина). Скомпилируйте и запустите приложение, и вы заметите, что стало невозможным вводить нецифровые символы в поле ввода.

Свойство KeyPreview формы показывает, передаются ли нажатия клавиш сначала в форму, а потом уже к выбранному компоненту. Если свойство KeyPreview равно FALSE (ложно), то нажатия клавиш пойдут прямиком к компоненту, имеющему фокус ввода. Если же свойство KeyPreview равно TRUE (как мы только что сделали), то нажатия клавиш будут приходить сначала к обработчику нажатий уровня формы, такому как наш метод, а только потом будут направлены к компоненту с фокусом ввода. Это позволяет нам просматривать нажатия клавиш и модифицировать под свои нужды. Это возможно из-за того, что обработчики нажатий получают параметр символ по ссылке (char &Key), что позволяет методу модифицировать значение символа.

Borland C++ Builder (+CD). Библиотека программиста 74

Имея это ввиду, создайте обработчик для второго переключателя (Разрешена десятичная точка) и добавьте в обработчик следующий код:

void __fastcall TForm1::RadioButton2Click(TObject *Sender)

{

Label1->Caption =

"Вводите цифры или десятичную точку: "; Edit1->OnKeyPress = Edit1Keypress2;

}

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

void __fastcall TForm1::EditKeyPress2(TObject *Sender, char &Key)

{

if ( !isdigit(Key) && Key != '.') Key = 0;

}

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

BOOL bFlag = strchr( Edit1.Text.c_str(), '.');

Тогда если логическое значение bFlag равно TRUE (истина), то вы могли бы также запретить ввод.

Последний обработчик для поля ввода разрешает любые символы. Вы думаете, мы напишем обработчик, который каким-либо образом позволяет любые символы? Для этого есть более простой путь. Создайте обработчик для третьего переключателя и добавьте следующий код в обработчик события:

void __fastcall TForm1::RadioButton3Click(TObject *Sender)

{

Label1->Caption = "Вводите что угодно: "; Edit1->OnKeyPress = NULL;

}

Опять же, мы ставим подсказку о том, что за ввод разрешен. Однако, вместо того, чтобы назначить обработчику конкретный метод, мы ставим его в NULL. Это убирает обработку

события для этого компонента и передает старшинство стандартному обработчику компонента TEdit (который воспринимает все символы). По ходу дела вам нужно было добавлять прототипы обработчиков событий, которые мы создавали, в заголовочный файл. Если у вас есть вопросы, то вот переделанный заголовочный файл:

class TForm1 : public TForm

{

__published: // IDE-managed components TLabel *Label1;

TEdit *Edit1; TLabel *Label2;

Borland C++ Builder (+CD). Библиотека программиста 75

TEdit *Edit2;

TRadioButton RadioButton1;

TRadioButton RadioButton2;

TRadioButton RadioButton3;

void __fastcall Edit1KeyPress(TObject *Sender, char &Key);

void __fastcall Edit2Exit(TObject *Sender);

void __fastcall RadioButton1Click(TObject *Sender); void __fastcall RadioButton2Click(TObject *Sender); void __fastcall RadioButton3Click(TObject *Sender); private: // User declarations

void __fastcall Edit1KeyPress2(TObject *Sender, char &Key); public: // User declarations

__fastcall TForm1(TComponent *Owner);

}

Поскольку я добавил функцию Edit1KeyPress через CBuilder как обработчик события KeyPress, то она уже находится в части, управляемой CBuilder.

Проверка данных после ввода

Иногда вам не хочется или же вы не можете добавлять данные посимвольно в поле ввода. В таком случае вы предпочтете подождать, пока пользователь закончит с вводом данных, а потом проверять данные при попытке покинуть поле ввода. Это может быть ввод чисел из конкретного диапазона, ввод времени, даты или любых других специфичных данных.

CBuilder также может помочь вам в этих случаях в проверке данных после ввода. Для проверки данных после ввода их пользователем вам нужно добавить обработчик события OnExit для поля ввода. Для демонстрации как это делается, нужно добавить обработчик для события OnExit второго поля ввода, Edit2. Добавьте следующий код в этот новый обработчик:

void __fastcall TForm1::Edit2Exit(TObject *Sender)

{

BOOL bFlag = TRUE;

char *s = Edit2->Text.c_str();

// Во-первых, проверяем что все символы цифры for (int i=0; i<(int)strlen(s); ++i)

{

char c = s[i]; if ( !isdigit(c) ) bFlag = false;

}

// Смотрим, прошел ли тест if ( bFlag == false)

{

MessageBox(NULL,

"В это поле можно вводить только цифры!", "Ошибка", MB_OK);

Edit2->SetFocus(); return;

Borland C++ Builder (+CD). Библиотека программиста 76

}

// Теперь проверяем диапазон (1..10) long lVal = atol(Edit2->Text.c_str()); if ( lVal < 1 || lVal > 10)

{

MessageBox(NULL,

"В это поле можно вводить только значения от 1 до 10!", "Ошибка", MB_OK);

Edit2->SetFocus(); return;

}

}

Этот обработчик сначала проверяет, чтобы ввод содержал только цифры. Если этот тест не проходит, то появляется сообщение об ошибке и фокус ввода возвращается к полю ввода. Если же тест прошел, тогда мы проверяем, что значение находится в нужном диапазоне. Если нет, то опять же появляется сообщение об ошибке и фкус ввода возвращается к полю ввода.

Замечание

Если вы работали с Windows SDK, то вы вероятно пытались сделать что-то подобное, обрабатывая сообщение WM_KILLFOCUS. Если так, то вы, вероятно, знаете, что попытка отображения окна с сообщением во время обработки сообщения WM_KILLFOCUS приводит к бесконечному циклу, который в конце концов рушит программу. Не бойтесь этих преданий старины; CBuilder не имеет таких проблем. Вы можете отображать окна с сообщениями, менять фокус ввода или делать еще что угодно в обработчике события OnExit. Это современная эра программирования, а не темные годы.

Я надеюсь, вы получили хороший урок в этом примере по поводу проверки ввода. Давайте коротко повторим основные моменты проверка данных в полях ввода в CBuilder:

Для обработки отдельных нажатий клавиш нужно установить свойство формы KeyPreview в true.

Когда вы обрабатываете ввод посимвольно для полей ввода, ставьте значение символа в 0, чтобы не допустить его добавление в поле ввода.

Вы можете убрать обработку события, установив указатель на обработчик события для данного объекта в NULL. Так, чтобы убрать посимвольную обработку в поле ввода, вам нужно поставить

Edit1->OnKeyPress = NULL.

Для проверки ввода при попытке пользователя покинуть поле ввода обрабатывайте событие OnExit. В добавление к этому, если вам нужны какие-либо действия при переключении пользователя в поле ввода, обрабатывайте событие OnEnter.

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

Волоки, пока не уронишь

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

Borland C++ Builder (+CD). Библиотека программиста 77

находиться выбранные. Между двумя списками в окне диалога располагались две кнопки, озаглавленные >> и <<. Кнопка >> перемещала выбранные элементы из левого списка (все возможные варианты) в правый (выбранные). Это было довольно простое окно диалога, и я справился с ним за пару часов. После этого начались проблемы.

Первый же смотр продукта пользователем немедленно направил мое окно диалога на переделку. Во-первых, пользователю не нравились кнопки >> и <<. «Не очень понятно», — говорили они. «Хорошо», — сказал я, и поменял текст на кнопках на «Добавить» и «Убрать».

Как раз в то время популярной темой для обсуждения в мире Windows (это была система Windows 3.1) была концепция drag-and-drop (дословно: перетащить и уронить). Один из старших менеджеров, который имел некоторый опыт общения с продуктами под Windows, поднял эту тему. «Что нам здесь нужно», — сказал он, — «это система drag-and-drop между двумя списками». К сожалению, в этом действительно был смысл. Пользователь стал бы просто выбирать нужные элементы в левом списке и перетаскивать их в правый список. Мы предложили прототип окна диалога, сделанный с помощью посторонней утилиты, и пользователям он понравился. Было легко выбирать один или более элементов слева и перетаскивать их направо. Дискуссия была закрыта; программирование началось, и проблемы немедленно появились.

Когда все это происходило, в системе, используемой нами (это были классы MFC, но фактически могло быть что угодно), поддержки для drag-and-drop не существовало. Разработка drag-and-drop для двух списков означала создание собственного класса списка, наследующего от стандартного, проверку нажатия кнопки мыши и определение того, что могло быть выбрано в списке. Три дня прошло, пока я доблестно пытался разработать процедуру для двух списков. В конце концов у меня было нечто, в основном работающее без странных побочных результатов. Программа была предъявлена конечным пользователям, которым сразу понравилось то, что они увидели. Как сказал мне мой менеджер после презентации (почему, интересно, программистов никогда не приглашают на представление продукта?), было всего лишь несколько пожеланий. Во-первых, им хотелось иметь возможность помещать элементы в нужную часть второго списка. Другими словами, часть, «роняющая» элементы, должна была поддерживать позиционирование. «Хорошо»,

подумал я, — «это разумное требование». Второе требование, однако, было страшным: позволить пользователю перемещать элементы внутри одного и того же списка.

Если вы когда-либо делали список с возможностью drag-and-drop, вы, вероятно, сталкивались с этой проблемой. Задача не только в удалении существующего элемента, а также в вычислении места, в которое он попадет в новом списке. Это был страшный код, так как я мог только догадываться о том, в какую позицию попадет элемент. Это было вне нормальной функциональности списка, и поэтому мне пришлось погрузиться в Windows API, чтобы доделать эту работу.

Наконец, через три недели после первой демонстрации, окно диалога с двумя списками было завершено, и пользователь был удовлетворен. Еще через неделю проект был закрыт, и я остался без работы. В чем мораль этой истории? Я точно не знаю, но вещи, подобные этой, гораздо легче реализуются в CBuilder, чем это было в MFC.

Реализация drag-and-drop в списках

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

Замечание

Borland C++ Builder (+CD). Библиотека программиста 78

Здесь стоит отметить, что на самом деле люди имеют ввиду две вещи, когда говорят о 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 для обоих списков:

Borland C++ Builder (+CD). Библиотека программиста 79

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 (истина).

Borland C++ Builder (+CD). Библиотека программиста 80

Третья и последняя интересная вещь в примере такова, что вам, вероятно, сразу не видна. Это пример сработает в случае, когда источник и получатель операции 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:

Соседние файлы в предмете Программирование на C++