Телефонный справочник
Цель лекции
Закрепление работы с записями, типизированными файлами и сеткой строк TStringGrid.
Постановка задачи
В данной лабораторной работе нам предстоит написать приложение - телефонный справочник. Это приложение должно обладать всеми необходимыми для нашей задачи качествами:
- выводить контактные данные в виде таблицы;
- сохранять контакты в файл и считывать их оттуда;
- добавлять новый или редактировать (изменять) существующий контакт;
- удалять выбранный из списка контакт;
- сортировать список контактов по алфавиту.
Для работы с контактами создадим тип данных запись, будем использовать переменную этого типа и типизированный файл, в котором данные будут храниться в двоичном виде. Сами контакты будем выводить в сетку TStringGrid, для добавления и редактирования записей создадим отдельное окно - сетка позволит только просматривать и выбирать контакты. Приступим?
Реализация
Для начала откройте Lazarus с новым проектом. Форму, как обычно, называем fMain, сохраняем проект в папку 25-01 под именем telephones, модулю главной формы присваиваем имя Main. Пойдем дальше. Скорее всего, вы будете делиться этой программой с друзьями, поэтому имеет смысл сразу же отключить от проекта вставку отладочной информации. Если вы помните, нужно выбрать команду "Проект -> Параметры проекта", в разделе "Параметры компилятора" перейти на подраздел "Компоновка" и убрать флажок "Генерировать отладочную информацию для GDB". Это позволит нашей программе сразу "похудеть" - от 15 мегабайт готового продукта, до менее чем 2-х мегабайт.
Теперь займемся формой. В свойстве Caption напишите "Телефонный справочник", в BorderStyle выберите bsDialog (нам не нужно, чтобы пользователь менял размеры окна), а в свойстве Position - poDesktopCenter. Чтобы ваше приложение в точности соответствовало моему, выберите ширину формы Width 700 пикселей, а высоту Height 400 пикселей.
Теперь нам на форме потребуется простая панель инструментов. Установите обычную панель TPanel, очистите её свойство Caption, в свойстве Align выберите alTop, чтобы панель заняла весь верх формы, и в свойстве Height установите значение 27 - нам не нужна слишком высокая панель инструментов, узкая будет смотреться аккуратней.
На панель нам нужно установить четыре кнопки TSpeedButton с вкладки Additional Палитры компонентов. Кнопки TSpeedButton нам нужны, чтобы на них не переходил фокус ввода, в этом случае, всегда активной будет только сетка. Присвойте свойству Name кнопок соответственно, имена bAdd, bEdit, bDel и bSort. Первая "b" в именах означает, что это кнопка - button. В свойствах Caption этих кнопок напишите, соответственно, "Добавить контакт", "Редактировать контакт", "Удалить контакт" и "Сортировать список" (без кавычек, разумеется). Картинок на кнопки мы ставить не будем, обойдемся только пояснительным текстом. Свойству Top всех кнопок присвойте значение 2, чтобы они почти прижались к верхней границе панели, свойству Width присвойте 150, чтобы весь текст помещался на кнопках без проблем. Переместите их относительно друг друга так, чтобы от левого края панели и друг от друга их отделяло тоже примерно по 2 пикселя.
Теперь сетка. На свободное место формы, ниже панели, установите сетку TStringGrid. Поскольку нам часто придется обращаться к ней в процессе кодирования, укоротим имя в свойстве Name до SG. В свойстве Align сетки выберите alClient, чтобы сетка заняла всё оставшееся место. Теперь нужно изменить некоторые другие настройки. По умолчанию, в сетке установлено по пять колонок ColCount, и по пять строк RowCount. Колонок нам нужно только три: имя контакта, его телефон и примечание, какой это телефон (мобильный, домашний, служебный, соседский). Поэтому уменьшим количество колонок ColCount до 3. А строка нам нужна и вовсе одна, она будет служить заголовком колонок, и она будет фиксированной. Уменьшайте RowCount до 1.
Теперь о фиксированных строках и колонках. Фиксированная строка FixedRows нам нужна только одна, это значение по умолчанию. А фиксированные столбцы FixedCols нам и вовсе не нужны, тут поставьте ноль.
Остальные параметры сетки можно оставить без изменений. Если вы всё сделали правильно, у вас в результате должна получиться такая форма:
Пойдем дальше. Прежде, чем приступать к самому кодированию, давайте сделаем еще одну форму - окно редактора контактов. Командой Файл -> Создать форму создайте новую форму. Присвойте ей имя fEdit, нажмите кнопку "Сохранить все" и дайте модулю формы имя Edit.
В свойстве Caption напишите "Редактор контакта". Почему не "контактов"? Каждый раз мы будем загружать в это окно по одному контакту, поэтому и использовано единственное число. В свойстве BorderStyle также выберите bsDialog, в свойстве Position - poMainFormCenter, чтобы окно появлялось по центру главной формы, где бы она ни находилась. Размеры формы: ширина width = 400, высота height = 225 пикселей.
Теперь разместим на форме компоненты. Установите метку TLabel, в Caption которой напишите "Укажите имя контакта:".
Ниже установите строку TEdit. Переименуйте её в eName, ширину сделайте 380. Очистите свойство Text.
Ниже еще одну метку, с текстом в Caption "Укажите телефон:".
Ниже установите еще один TEdit. Переименуйте её в eTelephone, ширину сделайте 210. Очистите свойство Text.
Ниже еще одну метку, с текстом в Caption "Выберите тип телефона:".
Ниже нам потребуется список выбора TComboBox. Мы же не хотим, чтобы пользователю каждый раз самому приходилось писать "мобильный" или "служебный"? Переименуйте TComboBox в CBNote, откройте редактор свойства Items и там наберите следующие строки:
Мобильный Домашний Служебный Соседский
Если сможете придумать другие типы, можете дописать их сюда.
В свойстве ItemIndex компонента установите 0, чтобы в списке появилась первая строка "Мобильный". Ширину сделайте 210.
Ещё ниже установите на одном уровне кнопки TBitBtn. Первую переименуйте в bSave, в свойстве Kind выберите bkOK, чтобы на кнопке появилась картинка - зеленая галочка, убедитесь, что свойство ModalResult кнопки перешло в mrOk. В свойстве Caption напишите "Сохранить контакт". Чтобы текст нормально умещался, увеличьте её ширину до 160 пикселей.
Вторую кнопку переименуйте в bCancel, в свойстве Kind выберите bkCancel, а в Caption напишите "Не сохранять". Свойство ModalResult кнопки должно перейти в mrCancel. В результате, у вас должна получиться вот такая форма:
Чтобы более не возвращаться к этой форме, давайте сразу введем весь необходимый код формы, тем более что его совсем мало. Для кнопок нам вообще не нужно писать код. Когда мы изменили их свойства Kind, установив там bkOK и bkCancel, сразу изменилось и их свойство ModalResult, установившись, соответственно, в mrOK и mrCancel. И теперь нажатие на любую из этих кнопок приведет к закрытию формы, и к тому, что в ModalResult формы попадет значение ModalResult нажатой кнопки. То есть, мы сможем узнать, какой кнопкой пользователь закрыл окно.
Но нам нужно все же сделать некоторую подготовительную работу. Прежде, чем показывать форму, нам нужно будет перевести фокус ввода в первый TEdit, ведь от прежней работы с формой выделенным мог остаться и другой компонент - кнопка или список выбора, например. А это будет неудобно для пользователя, ему придется лишний раз кликать мышью, выделяя первую строку.
Выделите форму, щелкнув по свободному участку, в Инспекторе объектов перейдите на вкладку События и сгенерируйте событие формы OnShow - это событие возникает всякий раз перед показом формы - идеальное место для подготовительной работы. Код события совсем простой:
procedure TfEdit.FormShow(Sender: TObject); begin eName.SetFocus; end;
И с редактором контактов это все! Вся остальная работа будет происходить в главной форме.
В редакторе форм перейдите на вкладку Main - наша главная форма. Прежде всего, в разделе uses главной формы, через запятую, добавьте модуль edit - это наш редактор контактов. Если мы не добавим его в раздел uses, то не сможем с ним работать из главной формы.
Далее, перед разделом глобальных переменных, тем, где объявлена переменная формы fMain, создадим еще один раздел type, в котором опишем запись:
type Contacts = record Name: string[100]; Telephon: string[20]; Note: string[20]; end; //record
В этой записи три поля. Первое поле Name предназначено для имени контакта. Вы можете заметить, что оно ограничено размером 100 символов. Как уже говорилось в "Записи и вариант. Сетка строк TStringGrid" , в записи можно указывать неограниченный тип string, но тогда создать типизированный файл этого типа не получится. Поэтому у нас все строки имеют фиксированный размер. В поле Name пользователь будет вписывать имена контактов, например:
Анатолий Васильевич, директор Дима, друг Леночка, секретарь
Может показаться, что 100 символов для имени контакта - это перебор. Однако вспомните, что для текста у нас используется кодировка UTF-8, а в ней каждый символ кириллицы занимает 2 байта. То есть, если писать имена по-русски, реально можно вписать только 50 символов! А фамилии и имена бывают очень длинными, но 50 символов должно хватить. Для телефона мы указали 20 символов. Это обычно, цифры, скобки и знаки тире:
111-11-11 3-(222)-111-11-11
На каждый из этих символов требуется только 1 байт, так что 20 символов хватит за глаза для любого телефона.
Ну и, наконец, поле Note, в котором будет одно из значений:
Мобильный Домашний Служебный Соседский
Эти символы тоже занимают по 2 байта, самое длинное слово имеет 9 символов - 18 байт. А мы указали 20, так что должно хватить.
Далее, в разделе глобальных переменных, после описания самой формы, опишем глобальную переменную adres:
var fMain: TfMain; adres: string; //адрес, откуда запущена программа
Эта переменная понадобится нам позже, в нее мы запишем адрес, откуда была запущена программа, чтобы потом по этому адресу отыскать файл с контактами. Ранее мы говорили, что если не указывать адрес, то компьютер будет искать файл в текущей папке, то есть - в папке с программой. Однако так бывает не всегда. Если вы параллельно работаете с несколькими другими программами, и все они то и дело обращаются к файлам, то текущий адрес может измениться. Вот почему всегда лучше подстраховаться, и указать настоящий адрес - это гарантирует вашу программу от любых ошибок.
Далее, "научим" программу принимать новые данные и выводить их в сетку. Сгенерируйте событие OnClick для кнопки "Добавить контакт". Код события такой:
procedure TfMain.bAddClick(Sender: TObject); begin //очищаем поля, если там что-то есть: fEdit.eName.Text:= ''; fEdit.eTelephone.Text:= ''; //устанавливаем ModalResult редактора в mrNone: fEdit.ModalResult:= mrNone; //теперь выводим форму: fEdit.ShowModal; //если пользователь ничего не ввел - выходим: if (fEdit.eName.Text= '') or (fEdit.eTelephone.Text= '') then exit; //если пользователь не нажал "Сохранить" - выходим: if fEdit.ModalResult <> mrOk then exit; //иначе добавляем в сетку строку, и заполняем её: SG.RowCount:= SG.RowCount + 1; SG.Cells[0, SG.RowCount-1]:= fEdit.eName.Text; SG.Cells[1, SG.RowCount-1]:= fEdit.eTelephone.Text; SG.Cells[2, SG.RowCount-1]:= fEdit.CBNote.Text; end;
Давайте разбираться с кодом. Прежде всего, в редакторе контактов fEdit мы очистили текст из строк с именем и номером телефона. Ведь если пользователь уже добавлял или редактировал контакты, в этих строках останутся записи с прошлого сеанса. Затем свойству ModalResult формы мы присваиваем значение mrNone - нет результата. Делаем это затем, чтобы потом точно знать, каким образом пользователь закрыл окно редактора контактов. Если кнопкой "Сохранить контакт", то в этом случае ModalResult формы будет mrOk, если кнопкой "Не сохранять", ModalResult формы будет mrCancel. Если же он просто закрыл окно крестиком в правом верхнем углу окна или клавишами <Alt+F4>, то в этом случае ModalResult формы так и останется mrNone.
Далее, мы выводим окно редактора контактов, как модальное.
К следующей строке кода программа доберется, когда пользователь введет (или не введет) новый контакт и закроет окно редактора. Прежде, чем что-то добавлять в сетку, мы делаем проверку - а ввел ли пользователь имя нового контакта, и телефон? Ведь если он ничего не ввел, то и сохранять нечего. И если он ввел один только телефон, без имени, или имя без телефона - какой смысл сохранять такой контакт? Поэтому, если нет имени или телефона, мы просто выходим из процедуры:
if (fEdit.eName.Text= '') or (fEdit.eTelephone.Text= '') then exit;
Однако может случиться, что пользователь ввел и имя, и телефон, но потом передумал сохранять контакт и нажал кнопку "Не сохранять", или просто закрыл окно редактора. Поэтому нам нужно сделать еще одну проверку:
if fEdit.ModalResult <> mrOk then exit;
Здесь мы смотрим значение свойства ModalResult формы. Оно сможет стать mrOK только в одном случае - если пользователь нажал кнопку "Сохранить контакт". И если это не так, то выходим из процедуры, ничего не сохраняя.
Если мы не вышли из процедуры в предыдущих двух проверках, то это означает, что
- нам есть, что сохранять
- пользователь нажал "Сохранить контакт"
И если это так, значит, добавляем новую строку в сетку SG и заполняем три её колонки значениями из редактора контактов. Дополнительных проверок на существование текста примечания мы не делали, так как там в любом случае будет указан один из типов телефонов:
//иначе добавляем в сетку строку, и заполняем её: SG.RowCount:= SG.RowCount + 1; SG.Cells[0, SG.RowCount-1]:= fEdit.eName.Text; SG.Cells[1, SG.RowCount-1]:= fEdit.eTelephone.Text; SG.Cells[2, SG.RowCount-1]:= fEdit.CBNote.Text;
Сохраните проект и запустите его на выполнение. Если вы всё сделали верно, программа позволит вам добавлять новые контакты. Не обращайте внимания на недоработанный вид сетки - узкие столбцы без заголовков - это мы исправим чуть позже.