Коллекции (массивы) строк и компоненты для них
Списки выбора TListBox и TComboBox
В Lazarus есть еще два компонента, которые используют тип TStrings, или говоря иначе, массивы строк. Это списки выбора TListBox и TComboBox. Такие списки обычно используют в программах не для редактирования текста, а для выбора пользователем одного или нескольких строк. TListBox - это прямоугольный список, похожий на TMemo, а TComboBox - выпадающий список.
Чтобы познакомиться с ними, закроем текущий проект и создадим новый. Сразу же форму переименуем в fMain, сохраним проект в папку 14-02 под именем MyStrings, а модулю главной формы, как обычно, дадим имя Main. В свойстве Caption формы напишите Выбор города, свойство BorderStyle сделайте bsDialog, чтобы пользователь не мог изменять размеры окна. Свойство Position сделайте poDesktopCenter, чтобы окно появлялось по центру - такие мелочи нужно сразу же устанавливать, чтобы программа хорошо смотрелась. Размеры формы сделаем такими: Height = 210, Width = 340.
Свойства Name у компонентов в этом проекте мы менять не будем. Так будет наглядней при обращении к этим компонентам внутри кода. Хотя в реальных проектах рекомендуется давать компонентам соответствующие их назначению названия. Ну, вот вам пример. Если на форме всего одна кнопка, то её имя можно оставить по умолчанию - Button1. Итак понятно, о какой кнопке идет речь. А вот если их три, как в прошлом проекте, тогда разумней дать им понятные имена. bSave, bRead, и bClear - ясно, что речь идет о кнопках, раз первая b (button), и что это кнопки "Сохранить", "Читать" и "Очистить". Если у вас слабые знания английского - не беда, можете давать русские названия, но писать латиницей: kSohranit, kChitat и kOchistit (k - knopka). Поскольку в нашем примере все компоненты будут в единственном экземпляре, менять их свойства Name мы не будем.
Вернемся к проекту. На вкладке Standard Палитры компонентов найдите компоненты TListBox и TComboBox:
В левом верхнем углу формы установите TListBox. Обратите внимание на его свойства в Инспекторе объектов. Мы затронем только некоторые, специфичные для TListBox.
Columns | - количество столбцов в списке. Если свойство равно нулю или единице, то список выходит в один столбец. Если больше - то столько же будет и столбцов. Если все строки не умещаются в списке, полоса прокрутки будет появляться автоматически. Обычно это свойство не меняют - строки в один столбец используются чаще всего. |
ExtendedSelect | - расширенное выделение. Подразумевает, что пользователь может выделить сразу целый диапазон строк, если, удерживая <Shift>, щелкнет сперва по первой, а потом по последней строке диапазона. По умолчанию, равно True - такое выделение разрешено. В любом случае, пользователь может выделять выборочные строки, щелкая по ним поочередно и удерживая при этом <Ctrl>. Всё вышесказанное работает, только если свойство MultiSelect = True. Не будем изменять это свойство. |
Items | - главное свойство компонента, массив (коллекция) строк. То же самое, что и свойство Lines у компонента TMemo, имеет тот же тип TStrings, и соответственно, обладает теми же свойствами и методами. Редактор строк также открывается при нажатии на кнопку "…" справа от свойства. |
MultiSelect | - возможность выделения нескольких строк. По умолчанию, равно False - многострочное выделение запрещено. Если вы пожелаете разрешить пользователю многострочное выделение, то вам в коде придется обходить весь список, чтобы выявить выделенные строки. |
Sorted | - сортировка списка. Если равно True, список сортируется по алфавиту. |
Всё остальное примерно соответствует компоненту TMemo. Итак, установим у нашего ListBox1 следующие свойства:
- Left, Top = 10
- Height, Width = 150
- MultiSelect = True
Теперь в свойстве Items кнопкой "…" откройте редактор строк и внесите следующие строки (каждый город на отдельной строке):
Москва |
Санкт-Петербург |
Киев |
Вильнюс |
Будапешт |
Вена |
Париж |
Берлин |
Лондон |
Мехико |
Оттава |
Лос-Анжелес |
Сан-Франциско |
Нью-Йорк |
Если есть желание отсортировать список городов по алфавиту, установите Sorted = True.
Теперь займемся компонентом TComboBox. Установите его правее ListBox1. У него тоже не так много специфичных свойств:
ArrowKeysTraverseList | - при True (по умолчанию) разрешает перемещаться по списку кнопками со стрелками. Хотя у меня и при False так можно было перемещаться, может быть, в следующих версиях Lazarus будет иначе. |
AutoComplete | - при значении True позволяет выбирать элементы из списка, фильтруя их. То есть, если в строке списка вы ввели "А", то выйдут только те элементы, которые начинаются на "А". Если вы ввели следующую "б", останутся только те, что начинаются на "Аб". И т.д. К сожалению, в моей версии Lazarus (v1.0.10) эта возможность недоработана. Если в свойстве установить True и попытаться сделать такую фильтрацию, то выходит run-time ошибка. Я не знаю, будет ли эта ошибка проявляться у вас, ведь новые версии Lazarus выходят достаточно часто, а у вас, скорее всего, уже более новая версия. |
AutoCompleteText | - переключатели автозавершения ввода, раскрывающее свойство с пятью переключателями. При True переключатель включен, при False - выключен. Имеются следующие переключатели:
|
ItemIndex | - это очень важное свойство - индекс выделенной строки. По умолчанию равен -1, то есть, никакая строка не выделена. Индексация строк начинается с нуля, но поскольку, мы не будем вручную заполнять ComboBox1, то оставим -1. |
Items | - такой же массив строк, как и у TListBox. Его также можно заполнить в процессе проектирования, нажав на "…" и вызвав Редактор строк. Однако мы не будем сейчас это делать. Заполнять список и изменять ItemIndex мы будем программно, в коде, ведь нужно освоить и эту возможность! |
Sorted | - как и у TListBox, это свойство сортирует список по алфавиту. |
Text | - текст, который выводится в строке компонента. При изменении ItemIndex меняется и свойство Text. По умолчанию, там написано имя компонента. Но нам это не нужно, поэтому просто очистите это свойство. |
Итак, установим следующие параметры компонента ComboBox1:
Left = 177 Top = 10 Width = 150
Строки в свойстве Items мы не заполняли, другие свойства не изменяли.
Далее, нам понадобится простая кнопка TButton. Её параметры:
Left = 10 Top = 170 Width = 150 Caption = Копировать в ComboBox
И напоследок, нам понадобится TEdit, чтобы выводить в него выбранную в ComboBox1 строку. Его параметры:
Left = 177 Top = 172 Width = 150 ReadOnly = True (пользователь не может редактировать эту строку) Text = '' (то есть, очистить это свойство)
В результате у вас должна получиться такая форма:
Суть программы в следующем: пользователь выбирает в ListBox1 один или несколько городов, нажимает кнопку "Копировать в ComboBox". Мы программно обойдем весь список ListBox1, от первой до последней строки, и каждую выбранную пользователем строку добавим в список ComboBox1. Если мы что-то в него добавили, нам придется установить ItemIndex = 0, то есть, выделить первую строку списка (иначе сразу будет невидно, добавилось туда что-то или нет). Далее, если в строке ComboBox появился какой-то город, нам нужно будет продублировать его в строку Edit1.
Пример, в общем, простой, но он демонстрирует почти все возможности списков. Для реализации задуманного нам потребуется всего два события: нажатие OnClick на кнопку Button1, и изменение OnChange в компоненте ComboBox1. Сгенерируйте их, вот код этих событий:
procedure TfMain.Button1Click(Sender: TObject); var i: integer; //счетчик begin //очистим ComboBox: ComboBox1.Clear; //далее в цикле обходим весь ListBox, и если строка //выделена - копируем ее в ComboBox: for i:= 0 to ListBox1.Count -1 do if ListBox1.Selected[i] then ComboBox1.Items.Add(ListBox1.Items.Strings[i]); //если ComboBox не пустой, выделим первый элемент, иначе //не выделяем ничего: if ComboBox1.Items.Count > 0 then begin ComboBox1.ItemIndex:= 0; //копируем в Edit1 выделенную строку из ComboBox: Edit1.Text:= ComboBox1.Items.Strings[ComboBox1.ItemIndex]; end else ComboBox1.ItemIndex:= -1; end; procedure TfMain.ComboBox1Change(Sender: TObject); begin //копируем в Edit1 выделенную строку из ComboBox: Edit1.Text:= ComboBox1.Items.Strings[ComboBox1.ItemIndex]; end;
Комментарии достаточно подробны, но все же проясним некоторые моменты. Свойство Items у TListBox и TComboBox, а также свойство Lines у TMemo имеют тип TStrings, поэтому ведут себя похожим образом - что работает у одного компонента, будет работать и у другого. Помните, как мы очищали Memo1? То же самое можно сделать и с TListBox, и с TComboBox:
ComboBox1.Clear;
А вот, как мы реализовали обход всех строк в ListBox1:
//выделена - копируем ее в ComboBox: for i:= 0 to ListBox1.Count -1 do if ListBox1.Selected[i] then ComboBox1.Items.Add(ListBox1.Items.Strings[i]);
Свойство Count компонента ListBox1 возвращает количество строк в компоненте. В Инспекторе объектов его не видно, однако все типы TStrings имеют это свойство, а значит, мы сможем таким же образом узнать количество строки ив Memo, и в ComboBox. Поскольку индексация строк начинается не с единицы, а с нуля, то у последней строки списка номер индекса будет Count - 1.
Далее, свойство Selected возвращает True, если указанная в индексе строка выделена. В принципе, мы можем посмотреть выделена ли какая-то конкретная строка, например, девятая:
if ListBox1.Selected[8] then
но гораздо удобней, конечно, обойти весь список в цикле for, как это было сделано в проекте.
Далее мы смотрим - если текущая строка выделена, то мы копируем её содержимое в список ComboBox1:
ComboBox1.Items.Add(ListBox1.Items.Strings[i]);
Содержимое каждой конкретной строки можно получить через её свойство Items, которое содержит непосредственно массив строк Strings. Указав индекс нужной нам строки, мы получим её содержимое. Например, получить содержимое третьей строки у ComboBox и Memo можно было бы так:
s:= ComboBox1.Items.Strings[2]; s:= Memo1.Lines.Strings[2];
Метод Add() типа TStrings добавляет указанную строку в конец списка. Точно также мы могли бы добавить строку в конец списка Memo или ListBox:
Memo1.Lines.Add('Новая строка'); ListBox1.Items.Add('Новая строка');
Вот таким способом мы заполняем список ComboBox1 теми строками, которые были выделены в ListBox1. Однако, поскольку пользователь мог и не выделять никаких строк, и все же нажать кнопку, мы делаем проверку:
//если ComboBox не пустой, выделим первый элемент, иначе //не выделяем ничего: if ComboBox1.Items.Count > 0 then begin ComboBox1.ItemIndex:= 0; //копируем в Edit1 выделенную строку из ComboBox: Edit1.Text:= ComboBox1.Items.Strings[ComboBox1.ItemIndex]; end else ComboBox1.ItemIndex:= -1; end;
Тут все достаточно прозрачно: если список ComboBox1 пуст, то его свойство Count будет равно нулю. В этом случае, нам нужно присвоить свойству ItemIndex значение -1, то есть, никакая строка не выделена. Если попытаться установить значение 0, то есть, выделить первую строку, произойдет ошибка, ведь строк-то нет! Если все же строки есть, то мы, во-первых, выделяем в списке первую строку:
ComboBox1.ItemIndex:= 0;
и, во-вторых, мы копируем её в Edit1:
Edit1.Text:= ComboBox1.Items.Strings[ComboBox1.ItemIndex];
Тут происходит вот что: ComboBox1.ItemIndex вернет текущий индекс, то есть, индекс выделенной строки. А ComboBox1.Items.Strings[x] вернет содержимое строки под индексом x, как это происходило у ListBox1 в цикле.
И напоследок, в событии OnChange компонента ComboBox1 мы дублируем код копирования выделенного текста в Edit1 - это событие будет срабатывать, когда пользователь выберет в списке какой-то другой город.
Свойства и методы типа TSrings
В завершение рассмотрим основные свойства и методы типа TStrings - базового класса массива строк, широко используемого в некоторых компонентах. Это может быть как свойство Lines компонента TMemo, так и свойства Items компонентов TListBox и TComboBox. Контейнер TRadioGroup, который мы с вами изучали в "Логические типы, конструкции и компоненты" , также имеет свойство Items типа TStrings. Имеются различные свойства этого типа и у других компонентов, как стандартных, так и созданных сторонними разработчиками. К сожалению, создавать переменные такого типа нельзя, для этого служит другой класс - TStringList, который является наследником TStrings, и обладает более расширенными возможностями. Но о нем мы будем говорить уже в другой лекции. А сейчас рассмотрим основные возможности типа TStrings.
Наименования | Описание |
---|---|
Свойства | |
Count | Количество строк в списке |
Strings | Индексированный массив строк. К отдельной строке обращаются по её индексу, например Strings[0] - первая строка массива. |
Text | Содержит текст всех строк, включая символы перехода на новую строку, в виде единой строки. |
Методы | |
Add | Добавляет строку в конец списка и возвращает индекс добавленной строки. Впрочем, возвращаемое значение можно игнорировать, как это делали мы в примере. |
Append | Добавляет строку в конец списка, но в отличие от Add, не возвращает никакого значения. |
AddStrings | Добавляет в конец списка другой список строк, также имеющий тип TStrings. |
Clear | Очищает список. |
Delete | Удаляет из списка строку по её индексу. Например, удалить вторую строку из Memo можно так:
Memo1.Lines.Delete(1); |
Insert | Вставляет в список строку по указанному индексу. Весь остальной текст сдвигается вниз. Если текста нет, строка будет вставлена по индексу 0. |
LoadFromFile | Считывает строки из указанного файла и заполняет ими компонент. |
SaveToFile | Наоборот, считывает строки из компонента, и сохраняет их в указанный файл. Если файла нет, он будет создан, если есть - перезаписан. |