4. Стандартные элементы управления (вторая часть)
В настоящей главе мы рассмотрим элементы управления между которыми много общего — списки и комбинированные списки. И те, и другие, предназначены для выбора одной или нескольких альтернатив из некоторого набора.
Примечание: В данной главе слово «элемент» используется в двух различных смыслах: как элемент списка (item), и как элемент управления Windows (control), в том числе и сам список. Надеюсь, по контексту прозрачно, какой элемент имеется в виду.
4.1 Элемент управления «список» (класс «LISTBOX» )
Начнем с того, что создадим самый простой список и заполним его строками.
Вообще-то, стиль LBS_HASSTRINGS можно было и не указывать — он подразумевается по умолчанию, если не указаны стили LBS_OWNERDRAWxxx . Следует обратить внимание на стиль WS_VSCROLL , который создает вертикальную полосу прокрутки. У полос прокрутки в списках есть две особенности:
- Горизонтальные полосы прокрутки не создаются (очень интересно, почему?);
- Вертикальная полоса прокрутки видна только тогда, когда в ней есть необходимость (т.е. не все строки помещаются по высоте элемента), если не указан стиль LBS_DISABLENOSCROLL — тогда она показывается всегда, а когда не нужна — серой — недоступной.
Стиль LBS_NOTIFY требуется для того, чтобы мы могли получать сообщения когда пользователь будет взаимодействовать с нашими списками.
Lazarus Free Pascal — How to Use ComboBox & ListBox
Добавление строки производится при помощи специального сообщения LB_ADDSTRING . При этом в параметре wParam передается 0, а в lParam — указатель на добавляемую строку, которая должна оканчиваться нулевым символом (ASCIIZ, в Паскале — тип PChar ).
Если не брать во внимание варианты списка с ручной отрисовкой, то остается еще один — список со множественным выбором. Отличается стилем LBS_EXTENDEDSEL или LBS_MULTIPLESEL . В первом случае (второй список в прилагаемом примере) возможен множественный выбор при помощи клавиш Shift и Ctrl; а во втором — клик мышью устанавливает выделение на текущий элемент, если он не выделен, и снимает с уже выделенного, не меняя при этом состояния других элементов списка.
4.2 Выпадающие (комбинированные) списки (класс «COMBOBOX» )
Комбинированные списки предствляют собой сочетание списка для выбора и поля редактирования.
Создание комбинированных списков практически аналогично созданию списков обычных за исключением того, что:
- Используется класс «COMBOBOX» (как можно было догадаться);
- Используются соответствующие стили окна;
- Указываемая высота относится к суммарной высоте поля редактирования и выпадающего списка, высота поля формируется системой автоматически;
- Добавление строк производится не сообщением LB_ADDSTRING , а CB_ADDSTRING .
Существует три основных разновидности комбинированных списков:
- Простой комбинированный список — стиль CBS_SIMPLE — список элементов виден всегда. В нашем примере такого нет.
- Выпадающий список — стиль CBS_DROPDOWN — первый пример.
- Выпадающий список без возможности редактирования — только выбор — стиль CBS_DROPDOWNLIST — второй пример.
4.3 Работа со списками
Для того, чтобы правильно понимать сообщения от списков, следует разобраться поподробнее с сообщением WM_COMMAND . В предыдущих примерах нам было достаточно знать, что младшее слово wParam содержит идентификатор элемента, однако сейчас возникла необходимость различать сообщения о разных действиях со списками.
Уроки программирования в Lazarus. Урок №7. Работа с выподающими списками ComboBox и ListBox
Параметры сообщения WM_COMMAND :
- wNotifyCode — HiWord (wParam) — код события;
- wID — LoWord (wParam) — идентификатор элемента;
- hwndCtl — lParam — дескриптор окна элемента.
Для примера напишем обработку, которая при изменении выделения в первом списке загонит соответствующую строку в заголовок главного окна.
. . . . . WM_COMMAND : ProcessCommand (Wnd, HiWord (wParam), LoWord(wParam), HWnd(lParam)); . . . . .
Итак, по порядку. Сначала мы определяем, что сообщение пришло именно от нужного нам списка, то есть — ID_LISTBOX_SINGLE . Затем анализируем код события. Кроме LBN_SELCHANGE здесь могут быть следующие значения:
- LBN_DBLCLK — двойной щелчок по списку;
- LBN_KILLFOCUS — потеря списком фокуса ввода;
- LBN_SELCANCEL — снято выделение с одного из элементов списка;
- LBN_SETFOCUS — список получил фокус ввода.
Обратим внимание на отсутствие события на простой клик. Дело в том, что простой клик обязательно должен привести к одному из двух событий — SELCANCEL или SELCHANGE .
Ну, а определив, что произошло именно нужное нам событие мы начинаем действовать. Как уже привыкли — при помощи сообщений.
- Сообщение LB_GETCURSEL параметров не имеет, возвращаемое значение — индекс текущего выделения. Заметим, что правильно работает это сообщение только для списков с одиночным выбором, для списка с множественным выбором будет возвращен индекс элемента на котором находится курсор (обычно это такая пунктирная рамка), но для этой цели лучше использовать сообщение LB_GETCARETINDEX . Для списков множественного выбора можно проверить состояние каждого элемента сообщением LB_GETSEL .
- Сообщение LB_GETTEXTSEL использует один параметр — индекс элемента, передаваемый в wParam . Возвращает длину строки соответствующего элемента.
- Для того, чтобы получить сам текст элемента, используется сообщение LB_GETTEXT . wParam — индекс элемента, lParam — адрес буфера, куда строка будет помещена.
- Общим для всех окон сообщением WM_SETTEXT устанавливаем новый заголовок.
Конечно действия, которые можно совершать со списками, не ограничиваются получением названия текущей строки. Однако, их количество достаточно велико и у меня нет никакого желания здесь их разбирать. Рекомендую раздел SDK, который называется «List Box Messages».
4.4 Работа с выпадающим списком
Как и в предыдущем случае, различных действий с комбинированными списками много. Посему остановимся на небольшом специфическом примере, а именно — осуществим часто используемую фишку — автоподстановки подходящей строки при вводе символов в поле ввода.
. . . . . ID_COMBO_DROPDOWN : (SendMessage (Ctl, CB_FINDSTRING, 0, longint(str)) = CB_ERR) SendMessage (Ctl, CB_SELECTSTRING, 0, longint(str)); SendMessage (Ctl, CB_SETEDITSEL, 0, MAKELPARAM (len, -1))
Итак, во-первых — мы обрабатываем событие CBN_EDITCHANGE , которое возникает при изменении текста поля ввода пользователем.
Во-вторых, для работы с текстом в поле ввода мы используем стандартные сообщения WM_GETTEXT и WM_GETTEXTLENGTH . А вот дальше — пошла специфика:
- Сообщение CB_FINDSTRING позволяет нам определить, существует ли в списке строка, начинающаяся с введенных нами символов. Если не существует, то подставлять нечего.
- Сообщение CB_SELECTSTRING работает аналогично CB_FINDSTRING , но при этом еще и выбирает соответствующую строку.
- Сообщение CB_SETEDITSEL позволяет выделить в поле ввода произвольную подстроку. В этом сообщении HiWord (lParam) должно быть равно конечной позиции выделения ( -1 для того, чтобы выделить все до конца строки, как в нашем случае), а LoWord (lParam) — начальной позиции. Функция MAKEPARAM() соответствует сишному макросу и попросту делает из двух значений типа Word одно типа LongInt .
В нашем примере есть одна существенная проблема — не обрабатываются нажатия Delete и BackSpace. Это происходит потому, что они удаляют в первую очередь выделенную часть, после чего строка заново находится, подставляется и выделяется. Для того, чтобы их корректно обрабатывать требуется значительное усложнение примера — например, хранение промежуточной строки и снятие выделения вместе с хвостом в случае, если начало строки не изменилось. Хотя наиболее корректный способ — написать класс-наследник от «COMBOBOX» , или реализовать соответствующую функциональность с нуля — чтобы можно было адекватно реагировать на любые клавиши.
Аналогично предыдущему, для изучения всех возможностей комбинированных списков рекомендую раздел SDK «Combo Box Messages» и смежные.
4.5 Резюме
Listbox lazarus примеры программ
Компонент Panel1 (панель) должен иметь свойство Align = alRight.
Компонент ComboBox1 будем использовать для изменения размера шрифта в редакторе. У компонента ComboBox1 в поле свойств text следует ввести 10. В поле Items следует нажать маленькую кнопку и в появившемся текстовом редакторе ввести строки:
Для создания списка тэгов HTML следует в блокноте создать файл примерно такого содержания:
Файл следует сохранить в папке с проектом, с именем, например tag.txt.
Компоненту Memo1 следует присвоить значение Align = alClient.
Создадим событие для формы onCreate и загрузим в ListBox1 файл со списком тегов:
procedure TForm1.FormCreate(Sender: TObject);
begin
Memo1.clear;
ListBox1.Items.LoadFromFile(‘tag.txt’);
end;
Если всё сделано правильно, то при запуске проекта мы увидим такую картинку:
Для вставки тэгов в блокнот Memo1 создадим событие onDblClick для компонента ListBox1:
procedure TForm1.ListBox1DblClick(Sender: TObject);
begin
Memo1.Lines.Add(ListBox1.Items[ListBox1.ItemIndex]);
end;
Здесь, запись Memo1.Lines.Add означает — добавить строку в блокнот Memo1.
ListBox1.Items — строка из списка с номером [ListBox1.ItemIndex]. ItemIndex — номер выделенной строки в списке. Так как ListBox1.Items является массивом строк, то обращение к строке осуществляется как к элементу массива, по его номеру.
Запустим проект на исполнение и попробуем написать HTML страничку, вставляя тэги двойным щелчком в ListBox1.
Следующим компонентом для исследования будет ComboBox1. На закладке Events инспектора объектов следует создать (двойным щелчком) событие onChange (изменение содержимого редактора ComboBox1), и напишем следующие строки:
procedure TForm1.ComboBox1Change(Sender: TObject);
var
n: integer; // число — размер шрифта пользователя.
begin
if ComboBox1.Text = » then exit; // Если в редакторе нет данных то выход
try // Включаем режим отслеживания ошибок преобразования строки в число
n:= StrToInt(ComboBox1.Text); //Преобразуем строку в числовое значение
except // Если возникла ошибка преобразования, то выдаём сообщение
on Exception : EConvertError do
ShowMessage(Exception.Message);
end;
memo1.Font.Size:= n; // Присваиваем шрифту Memo1 новое значение
end;
Запускаем программу и убеждаемся, что всё работает нормально.
Как открывать или сохранять тексты в редакторе Memo1, мы изучили на прошлом уроке. Добавив соответствующие кнопки, получим полноценный HTML редактор.
Манипуляции со строками объектов ComboBox и ListBox.
Для доступа к содержимому списков существует несколько функций, которые можно использовать во время выполнения программы:
ItemIndex — возвращает номер выделенной строки в списке (счёт строк начинается с 0);
Count — возвращает число строк в списке;
Items[n] — возвращает или записывает строку с номером n;
Items.add(строка) — добавляет строку в конец списка;
Items.delete(n) — удаляет строку с номером n из списка;
Items.Insert(i, ‘Новая cтрока’) — вставляет новую строку перед строкой с номером i.
Для проверки перечисленных действий добавим в нашу программу текстовый редактор Edit1 и четыре кнопки, которые будут добавлять, заменять, удалять и вставлять текст в список тэгов:
Кнопка Добавить — button1;
Кнопка Заменить — button2;
Кнопка Удалить — button3;
Кнопка Вставить — button4;
procedure TForm1.Button1Click(Sender: TObject);
begin
if MessageDlg(‘Добавить запись?’, mtInformation, [mbOk,mbCancel],0) = mrOK
then
begin
ListBox1.Items.Add(edit1.text);
ListBox1.Items.SaveToFile(‘tag.txt’);
end;
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
if MessageDlg(‘Заменить запись?’, mtInformation, [mbOk,mbCancel],0) = mrOK
then
begin
ListBox1.Items[ListBox1.ItemIndex]:= edit1.Text;
ListBox1.Items.SaveToFile(‘tag.txt’);
end;
end;
procedure TForm1.Button3Click(Sender: TObject);
begin
if MessageDlg(‘Удалить запись?’, mtInformation, [mbOk,mbCancel],0) = mrOK
then
begin
ListBox1.Items.delete(ListBox1.ItemIndex);
ListBox1.Items.SaveToFile(‘tag.txt’);
end;
end;
procedure TForm1.Button4Click(Sender: TObject);
begin
if MessageDlg(‘Вставить запись?’, mtInformation, [mbOk,mbCancel],0) = mrOK
then
begin
ListBox1.Items.Insert(ListBox1.ItemIndex, Edit1.text);
ListBox1.Items.SaveToFile(‘tag.txt’);
end;
end;
Таблица строк На закладке Additional находится компонент StringGrid — таблица, в ячейках которой можно отображать строковые данные. Число строк и столбцов в таблице можно задавать свойствами RowCount и ColCount соответственно.
FixedCols и FixedRows задают число фиксированных ячеек предназначенных для отображения заголовков колонок и строк. Доступ к содержимому ячеек осуществляется функцией Cells[ACol, ARow: Integer]: string, где ACol и ARow — номер колонки и номер строки соответственно.
Для включения возможности изменения размеров ячеек в процессе работы программы свойства OptionsgoRowSizing и OptionsgoColSizing следует установить в true.
Создадим таблицу 6×6 ячеек с заголовками, как это принято в Microsoft Excel.
Метка Label1 будет показывать координаты ячейки выбранной пользователем.
Создание заголовков ячеек поместим в событие создания формы OnCreate:
procedure TForm1.FormCreate(Sender: TObject);
var n: integer;
begin
for n:= 1 to 6 do StringGrid1.Cells[0,n]:= IntToStr(n);
for n:= 1 to 6 do StringGrid1.Cells[n,0]:= chr(n+64);
label1.Caption :=»;
end;
Фиксированные ячейки имеют индекс [0,0].
Ввод данных в ячейки можно осуществлять например, из строчного редактора Edit. Рассмотрим, как это можно осуществить. Поставим на форму редактор Edit1 и кнопку ввода (Button1).
Создадим событие onClick для кнопки Button1:
procedure TForm1.Button1Click(Sender: TObject);
begin
StringGrid1.Cells[StringGrid1.Col,StringGrid1.Row]:= edit1.Text;
end;
Для ввода данных следует выделить ячейку, ввести данные в Edit1 и нажать кнопку Ввод.
В таблицах StringGrid удобно исследовать числовые матрицы. Для ввода числовых данных в строковые ячейки будем пользоваться функциями преобразования:
FloatToStr(x: real) — преобразует число в строку.
StrToFloat(s: string) — преобразует строку в число.
Используя генератор случайных чисел, создадим матрицу, например 3 х 3 элемента и подсчитаем сумму главной диагонали матрицы. Результат поместим в правый нижний угол матрицы (ячейка 4,4).
Матрицу будем загружать при нажатии кнопки «New» (Button2). Создадим событие onClick на кнопку Button2:
procedure TForm1.Button2Click(Sender: TObject);
var
x,y: integer;
z: real;
begin
randomize;
for x:= 1 to 3 do
begin
for y:= 1 to 3 do
begin
StringGrid1.Cells[x,y]:= FloatToStr(random(200)*0.1);
end;
end;
z:= 0;
for x:= 1 to 3 do z:= z + StrToFloat(StringGrid1.Cells[x,x]);
StringGrid1.Cells[4,4]:= ‘Сумма = ‘ + FloatToStr(z);
end;
Источник: hi-intel.ru
6.3.10.3. Компоненты выбора
В этих компонентах можно организовать выбор каких-то элементов из списка. Весь список содержится в свойстве Items и имеет тип TString . Эле- ментами списка являются строки. Строки нумеруются, начиная с нуля. Доступ к строке осуществляется с помощью указания индекса элемента в свойстве Items , например Items[k] или свойства Items.Strings , например Items.Strings[k] .
Компонент TListBox
Рассмотрим компонент TListBox . Он расположен на странице Standard . Основные свойства компонента: MultiSelect – признак множественного выбора. Если MultiSelect = true , то разрешается выбор одновременно нескольких элементов.
Глава 6 Программирование приложений с графическим интерфейсом ____________________________________________________________________ ExtendedSelect – если ExtendedSelect= true и MultiSelect= true , то выбор нескольких элементов можно производить стандартным спосо- бом, т.е. при нажатой клавише Shift можно выбрать несколько элементов, расположенных подряд, а при нажатой клавише Ctrl выбрать элементы в произвольном порядке.
ItemIndex – индекс выбранного элемента (при MultiSelect= false ). Если выбрано несколько элементов ( MultiSelect= true ), то содержит ин- декс элемента, на котором установлен фокус.
Selected[i] – если выбран элемент с индексом i, то значение этого свой- ства равно true .
Sorted – если равно true , то элементы компонента автоматически сорти- руются.
Рассмотрим несколько примеров для того, чтобы освоить простейшую тех- нику работы с этим компонентом. Поместите на форму два компонента TListBox и три кнопки, так как показано на рис. 6.61. Заполним программно ListBox1 содержимым текстового файла, в кото- ром содержатся фамилии, допустим студентов. Затем будем просто помещать выбранные элементы в ListBox2 . Сначала реализуем выбор одиночного эле- мента.
6.3 Визуальное программирование в среде Lazarus ____________________________________________________________________ Рис. 6.61. Форма приложения Вот код этой программы: unit Unit1; interface uses Classes, SysUtils, FileUtil, LResources, Forms, Controls, Graphics, Dialogs, StdCtrls; type < TForm1 >TForm1 = class(TForm) Button1: TButton; Button2: TButton; Button3: TButton; ListBox1: TListBox; ListBox2: TListBox; procedure Button1Click(Sender: TObject); procedure Button2Click(Sender: TObject); procedure Button3Click(Sender: TObject);
Глава 6 Программирование приложений с графическим интерфейсом ____________________________________________________________________ procedure FormCreate(Sender: TObject); private < private declarations >public < public declarations >end; var Form1: TForm1; implementation < TForm1 >procedure TForm1.Button2Click(Sender: TObject); begin with ListBox1 do begin if ItemIndex >= 0 then ListBox2.Items.Add(Items[ItemIndex]); end; end; procedure TForm1.Button1Click(Sender: TObject); begin ListBox2.Clear; end; procedure TForm1.Button3Click(Sender: TObject); begin Close; end; procedure TForm1.FormCreate(Sender: TObject); var tfile: TStringList;
6.3 Визуальное программирование в среде Lazarus ____________________________________________________________________ str: string; begin tfile:= TStringList.Create; tfile.LoadFromFile(‘List.txt’); str:= tfile.Text; str:= SysToUTF8(str); // преобразование в кодировку UTF-8 with ListBox1 do begin Items.Text:= str; end; tfile.Free; end; initialization end. Если установить в ListBox2 свойство Sorted = true , то элементы будут выведены в отсортированном виде. При добавлении нового элемента, он будет помещен в нужное место автоматически. В этой реализации, если при одном и том же выбранном элементе нажать на кнопку «Копировать» несколько раз, то этот элемент попадет в ListBox2 также несколько раз, т.е. строки в ListBox2 окажутся не уникальными. Чтобы элементы в ListBox2 не повторялись, напишем функцию, кото- рая проверяет не содержится ли уже этот элемент в ListBox2 . Если такой элемент имеется, функция возвращает true , если нет, то false . unit Unit1;
Глава 6 Программирование приложений с графическим интерфейсом ____________________________________________________________________ interface uses Classes, SysUtils, FileUtil, LResources, Forms, Controls, Graphics, Dialogs, StdCtrls; type < TForm1 >TForm1 = class(TForm) Button1: TButton; Button2: TButton; Button3: TButton; ListBox1: TListBox; ListBox2: TListBox; procedure Button1Click(Sender: TObject); procedure Button2Click(Sender: TObject); procedure Button3Click(Sender: TObject); procedure FormCreate(Sender: TObject); function Search(str: string): boolean; private < private declarations >public < public declarations >end; var Form1: TForm1; implementation < TForm1 >function TForm1.Search(str: string): boolean; var
6.3 Визуальное программирование в среде Lazarus ____________________________________________________________________ i: integer; begin Result:= false; for i:= 0 to ListBox2.Count — 1 do if ListBox2.Items[i] = str then begin Result:= true; exit; end else Result:= false; end; procedure TForm1.Button2Click(Sender: TObject); begin with ListBox1 do begin if (ItemIndex >= 0) and (not Search(GetSelectedText)) then ListBox2.Items.Add(Items[ItemIndex]); end; end; procedure TForm1.Button1Click(Sender: TObject); begin ListBox2.Clear; end; procedure TForm1.Button3Click(Sender: TObject); begin Close; end; procedure TForm1.FormCreate(Sender: TObject); var
Глава 6 Программирование приложений с графическим интерфейсом ____________________________________________________________________ tfile: TStringList; str: string; begin tfile:= TStringList.Create; tfile.LoadFromFile(‘List.txt’); str:= tfile.Text; str:= SysToUTF8(str); // преобразование в кодировку UTF-8 with ListBox1 do begin Items.Text:= str; end; tfile.Free; end; initialization end. Реализуем теперь множественный выбор. Обработчик события Button2Click несколько видоизменится, т.к. при множественном выборе для того, чтобы определить выбран ли этот элемент или нет, необходимо ис- пользовать свойство Selected . unit Unit1; interface uses Classes, SysUtils, FileUtil, LResources, Forms, Controls, Graphics, Dialogs, StdCtrls;
6.3 Визуальное программирование в среде Lazarus ____________________________________________________________________ type < TForm1 >TForm1 = class(TForm) Button1: TButton; Button2: TButton; Button3: TButton; ListBox1: TListBox; ListBox2: TListBox; procedure Button1Click(Sender: TObject); procedure Button2Click(Sender: TObject); procedure Button3Click(Sender: TObject); procedure FormCreate(Sender: TObject); function Search(str: string): boolean; private < private declarations >public < public declarations >end; var Form1: TForm1; implementation < TForm1 >function TForm1.Search(str: string): boolean; var i: integer; begin Result:= false; for i:= 0 to ListBox2.Count — 1 do if ListBox2.Items[i] = str then
Глава 6 Программирование приложений с графическим интерфейсом ____________________________________________________________________ begin Result:= true; exit; end else Result:= false; end; procedure TForm1.Button2Click(Sender: TObject); var i: integer; begin with ListBox1 do for i:= 0 to Items.Count — 1 do begin if (Selected[i]) and (not Search(Items[i])) then ListBox2.Items.Add(Items[i]); end; end; procedure TForm1.Button1Click(Sender: TObject); begin ListBox2.Clear; end; procedure TForm1.Button3Click(Sender: TObject); begin Close; end; procedure TForm1.FormCreate(Sender: TObject); var tfile: TStringList;
Источник: studfile.net