ComboBox – выпадающий список
Такие списки вы часто встречаете при работе с Windows и различными программами. Действуют и работают они также как ListBox, с той только разницей, что выглядят по-другому, и не позволяют выбрать несколько элементов сразу. Создайте новое приложение, добавьте туда только один ComboBox и один Edit:
Рис. 12.3. Внешний вид приложения
Вызовите редактор строк ComboBox (свойство Items) Впишите несколько городов:
Москва
Санкт-Петербург
Киев
Минск
Ташкент
Душанбе
Свойство ItemIndex, которое, как мы знаем, указывает индекс выделенной строки, по умолчанию установлено в -1. Это означает, что ни одна строка не выбрана. Если установить его в 0, то в поле ввода текста появится первая строка списка. Оставим -1, чтобы строк не было видно.
Создадим обработчик события OnChange для компонента ComboBox, и там напишем только одну строку:
Edit1.Text := ComboBox1.Items.Strings[ComboBox1.ItemIndex];
Как видите, это почти точная копия работы с ListBox. Сохраните пример в новую папку, откомпилируйте и попробуйте как он работает.
Управление циклами
Вы знаете уже практически все циклы – for..do, while..do, repeat..until. Однако этими циклами можно еще и управлять. Для этого служат директивы break и continue.
Break – прерывание цикла. Если внутри цикла встретится такой оператор, происходит немедленный выход из цикла. Как правило, этот оператор используют совместно с управляющей структурой if, например:
if a <> b then break;
Следовательно, если возникнет какое-то недопустимое для цикла условие, вы всегда имеете возможность прервать цикл досрочно.
Continue – прерывание текущего шага цикла. В отличие от break, continue не прекращает цикл вовсе, а лишь прерывает дальнейшую обработку этого шага цикла, после чего цикл сразу начинается со следующего шага. Способ применения такой же, как у break. Рассмотрим работу continue на практическом примере.
Нам нужно разделить число 10 на число от -3 до 3 включительно, и результат вывести в ListBox. Поскольку выводить будем также целые числа, нам поможет функция Round(), которая принимает вещественное число, округляет его до ближайшего целого и это целое возвращает. Также мы знаем, что на ноль делить нельзя, это ошибка. Для того, чтобы не допустить это деление, мы прервем этот шаг цикла с помощью директивы continue.
Создайте новое приложение. Установите на форму ListBox, а под ним – кнопку:
Рис. 12.4. Внешний вид приложения
При нажатии на кнопку, напишите следующий код:
procedure TForm1.Button1Click(Sender: TObject);
var
i, r : Integer;
begin
for i := -3 to 3 do begin
if i = 0 then begin
ListBox1.Items.Add('На ноль делить нельзя!');
Continue;
end; //if
r := Round(10/i);
ListBox1.Items.Add('10/'+IntToStr(i)+'='+IntToStr(r));
end; //for
end;
В тот момент, когда счетчик i станет равным 0, выполнится тело условия if, и после оператора Continue цикл сразу перейдет на новый виток, пропустив деление.
Самостоятельное задание
В примере мы жестко задали условия: делить именно число 10 на диапазон чисел от -3 до 3. Измените пример так, чтобы пользователь мог сам указать какое число нужно делить, и на какой диапазон. Это несложное задание, однако, придется подключить к работе мозги.
Лекция 13. Диалоги
В этой лекции вы познакомитесь с диалогами, изучите компоненты OpenDialog, SaveDialog, FontDialog и ColorDialog. Создадите вторую версию текстового редактора, обладающего вполне профессиональными возможностями. Научитесь пользоваться директивой with.
Диалоги
Что такое диалоги в Delphi? Это невизуальные, то есть, невидимые пользователю компоненты, которые выполняют стандартные для Windows диалоги пользователя и программы. Например, диалог открытия или сохранения файла, диалог выбора шрифта или цвета, и т.п. Любая серьезная программа содержит такие диалоги. Работа с ними несложная, каждый диалог содержит буквально по два свойства, которые нам будут нужны. Изучим работу диалогов на примере.
Открываем новый проект. Сразу же форму переименовываем в fMain, в свойстве Caption пишем "Мой блокнот". Сохраните проект под именем Editor. Это будет более профессиональная версия редактора текстов, какой мы уже делали. Считайте, что это вторая версия программы, что и напишете в окне fAbout.
Установите на форму компонент Memo, удалите весь текст из его свойства Lines. Поверх Memo установите MainMenu и PopupMenu. Перейдите на вкладку Dialogs в палитре компонентов, и также поверх Memo установите OpenDialog, SaveDialog, FontDialog и ColorDialog. Все эти компоненты не визуальные, пользователь их видеть не сможет. У Memo сразу же укажите свойство Align=alClient, а свойство ScrollBar=ssVertical. Рекомендую для лучшего вида выбрать шрифт (свойство Font) Times New Roman, а размер = 12. На всех компьютерах с русской Windows есть этот шрифт, однако если вы будете писать программу для продажи в Интернет, лучше оставить шрифт по умолчанию, это гарантирует, что он будет читаться на всех компьютерах в мире.
Во всех диалогах есть один метод, который нас интересует – Execute. Это метод логического типа, он возвращает True, если диалог с пользователем произошел успешно (например, пользователь выбрал открываемый файл), и False в противном случае (например, пользователь отказался от выбора файла). В связи с этим, диалоги обычно применяют вместе с проверкой:
if OpenDialog1.Execute then...
В диалогах, связанных с файлами (OpenDialog – открыть файл, и SaveDialog – сохранить файл), есть свойство FileName, которое возвращает строку – адрес и имя выбранного файла. Вот, собственно, и все, что нам нужно от этих диалогов!
Диалог FontDialog в свойстве Font возвращает выбранный шрифт. Это свойство непростое, оно имеет тип TFont, и присвоить его можно только другому шрифту, например:
Memo1.Font := FontDialog1.Font;
Точно также, диалог ColorDialog возвращает свойство Color – цвет, имеющее тип TColor, и это свойство можно присвоить только объекту, имеющему такой же тип:
Memo1.Color := ColorDialog1.Color;
Продолжим наш проект. Откройте редактор главного меню. Создайте раздел "Файл" и подразделы "Открыть", "Сохранить", "Закрыть", "-" и "Выход". Создайте раздел "Параметры" и подразделы "Выбрать шрифт" и "Выбрать цвет". Создайте раздел "Справка" и подраздел "О программе".
Теперь открываем редактор PopupMenu и вписываем разделы "Открыть", "Сохранить", "Закрыть", "-" и "Выход". Сразу же на форме выделяем саму форму (это можно сделать в окне Object – TreeView), и в свойстве PopupMenu выбираем наше меню. Теперь это меню откроется, если пользователь щелкнет правой кнопкой по любому месту на форме.
Теперь нам нужно настроить фильтры в диалогах OpenDialog (Открыть) и SaveDialog (Сохранить). Фильтры позволяют отображать в этих диалогах только нужные форматы файлов, и для этого выбора используется маска файла. К примеру, маска *.* будет отображать файлы всех типов! Дважды щелкнув по свойству Filter диалога OpenDialog, откройте редактор фильтров. В первой колонке напишите "Текстовые документы", во второй – "*.txt". В строке ниже укажите "Все файлы", а во второй колонке – "*.*". Тоже самое сделайте для диалога SaveDialog:
Рис. 13.1. Настройка фильтра файловых диалогов
Теперь, открывая файл, вы увидите только эти два типа файлов. Подумаем о том, что программа должна знать – изменился ли текст в Memo. Ведь пользователь может закрыть программу и не сохранить текст, а потом будет ругать программиста за то, что он этого не предусмотрел. При этом имеем в виду, что у нас есть много команд меню, значит, будет много процедур. А чтобы дать программе знать, изменился ли текст, разумнее всего создать переменную логического типа – изменился текст, присваиваем ей True, иначе False. Чтобы с этой переменной можно было работать из всех процедур, она должна быть глобальной. Делаем глобальную переменную перед словом implementation:
izmen : Boolean; //изменился ли текст в Memo
Событию onChange компонента Memo присвойте строку:
izmen := True;
Как только изменится текст в Memo, переменная тут же будет выдавать истину.
Теперь еще один момент – программа должна знать имя и адрес открытого файла. Если имени файла нет, то программа будет выводить диалоговое окно, а если мы файл открывали и имя есть, то программа просто будет перезаписывать тот файл без вывода диалогового окна. Стало быть, делаем еще одну глобальную переменную:
myfile : String; //Адрес и имя открытого файла
Я специально не беру широко распространенные слова "File" или "FileName", так как они могут быть зарезервированными или в компонентах могут быть свойства с такими именами, в результате получится конфликт названий.
Соображаем дальше. Открыть файл можно будет командой меню "Файл – Открыть", либо командой PopupMenu "Открыть". Стало быть, нам придется дважды писать один и тот же код? А если он будет большим и сложным? Можно конечно и скопировать его, компилятор это выдержит, и программа будет работать нормально. А как же оптимизация кода? Два одинаковых кода в программе будут занимать в два раза больше места в памяти и на диске! Для этого мы имеем пользовательские функции и процедуры.
Вспоминаем – функцию или процедуру мы должны описать ВЫШЕ того места, где будем ее использовать, значит, первую нашу процедуру мы должны описать в разделе implementation, прямо под строчкой {$R *.dfm}.
Сейчас вы узнаете еще кое-что новенькое. В таких вот пользовательских процедурах и функциях вы не можете напрямую обращаться к компонентам формы, поскольку Ваши процедуры и функции самой форме не принадлежат. Если Вы введете
Memo1.
то компилятор сразу выдаст ошибку. Обращаться к компоненту нужно, указав сначала имя формы, на которой он находится:
fMain.Memo1.
Однако такой код будет не слишком удобен – каждый раз придется обращаться к компонентам через форму. Лишний набор кода, излишне длинный текст. Выход есть – функция with (с). Эта функция имеет вид:
with fMain do begin
...
end;
где fMain – имя формы с нужными нам компонентами. Теперь между скобок begin...end этой конструкции мы можем обращаться к компонентам формы напрямую. Пишем общую для всех процедуру открытия файла:
{Процедура открытия файла}
procedure Otkrivaem;
begin
with fMain do begin //делать вместе с формой
if OpenDialog1.Execute then begin //если диалог выполнен
//присваиваем переменной myfile адрес и имя выбранного файла:
myfile := OpenDialog1.FileName;
//читаем этот файл в Memo:
Memo1.Lines.LoadFromFile(myfile);
izmen := False; //файл только открыт, изменений еще нет
end; //if
end; //with
end;
Теперь создаем обработчик событий для команды меню "Файл – Открыть". Там вызываем нашу процедуру:
Otkrivaem;
Тоже самое делаем для команды PopupMenu "Открыть".
Дальше – сложней. Открыть файл просто, но вот при сохранении нам придется учитывать много вещей:
1. Пользователь может сохранять новый файл, который он только что набрал. То есть, переменная myfile пуста и не содержит имя файла. В таком случае придется выводить диалог SaveDialog1, чтобы пользователь указал имя файла. Когда он укажет имя, присвоить его переменной myfile и сохранить Memo в указанный файл.
2. Пользователь может сохранять новый файл, как в предыдущем примере. Мы выведем диалог, но он его не завершит – нажмет кнопку "отмена" или красный крестик в верхней части окна справа. Значит, сохранять ничего не нужно, мы ведь не знаем, куда сохранять! Но придется его предупредить, что файл не сохранен!
3. Пользователь может сначала открыть файл и изменить его, а потом сохранять. В переменной myfile это имя уже есть, диалог не нужен, просто перезаписываем этот файл.
4. Пользователь может сохранять файл, текст которого не изменялся. В таком случае, просто игнорируем его команду!
Видите? Пользоваться диалогами несложно, но как много приходится учитывать во время проверок действий пользователя! Нужно учитывать каждую мелочь, иначе вам скажут, что ваша программа полна "багов" - мелких неисправностей. Итак, пишем процедуру сохранения, сразу под процедурой открытия:
{Процедура сохранения файла}
procedure Sohranyaem;
begin
with fMain do begin
//если изменений не было, выходим из процедуры,
//ничего не делая:
if not izmen then Exit;
//Если файл уже открывался, и в переменной myfile
//есть его адрес и имя, просто перезаписываем этот файл:
if myfile <> '' then begin
Memo1.Lines.SaveToFile(myfile);
izmen := False;
Exit; //выходим после сохранения
end; //if
{Файл новый, переменная myfile еще пуста. Дальше есть два варианта:
пользователь выберет или укажет файл в диалоге, или не сделает этого}
//если выбрал файл:
if SaveDialog1.Execute then begin
//прописываем адрес и имя файла в переменную:
myfile := SaveDialog1.FileName;
//если нет расширения *.txt то добавляем его:
if copy(myfile, length(myfile)-4, 4) <> '.txt' then
myfile := myfile + '.txt';
//сохраняем Memo в указанный файл:
Memo1.Lines.SaveToFile(myfile);
//файл сохранен, изменений нет:
izmen := False;
end //if
//если не выбрал файл:
else ShowMessage('Вы не указали имени файла, файл не сохранен!');
end; //with
end;
Приведенный выше код имеет достаточно подробные комментарии, так что все должно быть понятно. Новое, что вы могли увидеть – директива Exit. Эта директива досрочно завершает работу процедуры (или функции). То есть, если выполнено условие и отработан нужный код, Exit заставляет процедуру завершить работу. Остальной код, который есть в этой процедуре, не выполняется.
Теперь мы можем создать обработчик главного меню "Файл – Сохранить", и там прописать вызов этой процедуры:
Sohranyaem;
Тоже самое делаем для команды PopupMenu "Сохранить".
Далее идет команда главного и всплывающего меню "Выход". Тут все просто, в обоих случаях пишем команду
Close;
Далее идет раздел "Параметры". Создаем обработчик "Выбрать шрифт", там все просто:
if FontDialog1.Execute then
Memo1.Font := FontDialog1.Font;
Точно также и с подразделом "Выбрать цвет":
if ColorDialog1.Execute then
Memo1.Color := ColorDialog1.Color;
Далее вернемся в раздел "Файл". Здесь у нас остался подраздел "Закрыть". Создаем для него обработчик, и запишем текст:
{если файл не сохранен, предупреждаем пользователя об этом. Если он
желает сохранить, то вызываем процедуру сохранения:}
if izmen then
if Application.MessageBox('Файл изменен. Сохранить?', 'Внимание!',
MB_YESNO+MB_ICONQUESTION) = IDYES then Sohranyaem;
//теперь закрываем текущий файл:
Memo1.Clear; //очищаем Мемо
myfile := ''; //нет имени текущего файла
izmen := False; //нет изменений
Здесь не так много текста, и он вполне понятен, поэтому не будем для него делать отдельную процедуру. Просто скопируйте текст, создайте обработчик событий для "Закрыть" PopupMenu и вставьте этот же текст туда.
Примерно тоже самое нам придется сделать и для события формы onClose – ведь мы заранее не можем знать, каким образом пользователь закроет форму, через команду главного меню "Файл – Закрыть", через команду всплывающего меню "Закрыть", нажмет ли он красный крестик наверху или нажмет горячие клавиши <Alt + F4>! Поэтому для проверки – есть ли в программе несохраненный текст, мы используем событие onClose, которое случается при попытке закрыть форму, то есть в данном случае всю программу. Здесь код будет таким же, как и в предыдущем примере, но раз мы закрываем всю программу, нам не нужно выполнять блок закрытия файла. Просто проверим, есть ли изменения, и если они есть, выведем запрос – сохранить ли их. Если пользователь ответит "Да", значит, вызовем процедуру сохранения:
{если файл не сохранен, предупреждаем пользователя об этом. Если он
желает сохранить, то вызываем процедуру сохранения:}
if izmen then
if Application.MessageBox('Файл изменен. Сохранить?', 'Внимание!',
MB_YESNO+MB_ICONQUESTION) = IDYES then Sohranyaem;
Теперь введем еще одну команду для обоих меню – "Очистить". Она будет предназначена на случай, если пользователь введет абракадабру, а потом решит очистить текст. Я намеренно оставил это действие на конец, чтобы вы могли усвоить, что в процессе работы над программой меню может изменяться!
Откройте редактор MainMenu, в разделе "Файл" в самом низу добавьте подраздел "Очистить". Затем мышкой перетащите его на место линии разделов, при этом линия опустится вниз, а новый подраздел встанет на ее место:
Рис. 13.2. Вид главного меню
Создайте для команды "Очистить" обработчик, и впишите туда текст:
{MainMenu - Файл - Очистить}
procedure TfMain.N17Click(Sender: TObject);
begin
//очищаем Мемо:
Memo1.Clear;
//если открытого файла нет, то нет и изменений:
if myfile = '' then izmen := false
//иначе текст изменен:
else izmen := true;
end;
Сделайте то же самое для всплывающего меню – создайте раздел "Очистить", а в его обработчик скопируйте тот же текст.
Осталось создать модальное окно fAbout, где Вы укажете, что это текстовый редактор, второй версии. Сделайте это самостоятельно и привяжите вызов этой формы к команде главного меню "Справка – О программе".
Все, сохраняйте, компилируйте и проверяйте, как работает программа! У нас получился несложный, но вполне профессиональный редактор текстов. Одно замечание: текст мы сохраняем, но вот выбранный шрифт и цвет окна Вы сохранить не можете, об этом мы будем говорить на других лекциях, когда научимся сохранять параметры.