Первый раздел урока: Текстовый редактор, или Наберём и очистим

Приложения для работы с текстом (например, MS Word) играют важную роль в жизни большинства людей. С помощью таких приложений можно набирать и редактировать текстовые документы, сохранять их на диск, распечатывать и т. д.

Задачи урока

  • Работа с компонентом Memo (многострочный редактор).
  • Работа с компонентом CheckBox (переключатель).
  • Понятие компонента-контейнера.

Выполнение упражнения

Сегодня мы создадим простой текстовый редактор с возможностью очистки содержимого рабочей области (рисунок 1). Этот редактор может пригодиться при работе с текстовыми документами.

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

image095
Рисунок 1. Окно приложения

1. Дизайн приложения

1. Поместите компонент Memo  (вкладка Standard) на форму. Также добавьте компонент-контейнер Panel. В нём мы разместим кнопки, управляющие приложением. После этого очистите у компонента Panel свойство Caption и измените заголовок формы на надпись «Текстовый редактор». Примерный результат работы показан на рисунке 2.

image098
Рисунок 2

Компонент Panel обладает очень важной особенностью. Он является компонентом-контейнером. Если на него поместить другие компоненты, то их перемещение будет ограничено границами компонента-контейнера. При перемещении компонента-контейнера по форме всё его содержимое будет перемещаться вместе с ним.

3. Чтобы понять принцип работы компонентов-контейнеров, посмотрите на содержимое окна Дерево объектов (Object TreeView) (если его не видно на экране, нажмите комбинацию клавиш Ctrl+Shift+F11). В нём хранится список всех компонентов, размещённых на форме (рисунок 3а). Из него видно, что главный контейнер — это форма (Form1). На ней расположены два компонента: Memo1 и Panel1.

 а)image11  б) image17 в) image18
Рисунок 3

4. Увеличьте размер панели до размеров, в двое больших чем многострочный редактор. Измените контейнер у компонента Memo1 с Form1 на Panel1. Для этого в окне Дерево объектов методом Drag and Drop (Перетащить и оставить) перетащите компонент Memo1 из формы (Form1) на панель (Panel1), как показано на рисунке 3б. В результате вы увидите, что панель стала обладателем многострочного редактора Memo (рисунок 3в). Теперь перемещение панели приведёт и к перемещению многострочного редактора.

5. Измените контейнер у компонента Memo1 с Panel1 на Form1.

3. В принципе, текстовый редактор уже готов. Сохраните проект под именем Editor.

4. Запустите приложение и убедитесь, что вы можете легко набирать и редактировать текст в поле Memo.

Итак, наше приложение, созданное с помощью нескольких нажатий мыши, уже позволяет редактировать текст, и это «плюс». Рассмотрим «минусы»:

  • при запуске приложения компонент Memo содержит совершенно ненужную надпись «Memo1«;
  • при изменении размеров формы размер поля Memo не меняется, что выглядит некрасиво: при увеличении окна появляется много свободного места, а при уменьшении — появляются полосы прокрутки.

5. Чтобы удалить надпись «Memo1» в компоненте Memo, необходимо у него очистить свойство Lines (рисунок 3а). Это аналог свойства Text компонента Edit, только в данном случае текст можно вводить многострочный. Итак, в компоненте Memo напротив свойства Lines нажмите на кнопку с тремя точками. Откроется дополнительное окно (рисунок 3б). Удалите весь содержащийся там текст.

 а) image100 б)
Рисунок 3

6. Настроим компонент Memo так, чтобы его границы автоматически изменялись при изменении размеров формы с помощью свойства Align (переводится как «выровнять»). Как видно из рисунка 1, компонент Panel должен «прилипнуть» к верхней части окна (Align = alTop), а компонент Memo должен занять всю остальную площадь (Align = alCliеnt). Результат показан на рисунке 4а.

7. Запустите приложение и поменяйте размер формы. Размеры компонентов динамически изменятся в зависимости от её размеров. Закройте приложение.

 а) image104 б) image106
Рисунок 4

8. В компонент Panel добавьте четыре кнопки Button и измените их заголовок, как показано на рисунке 4б. Назначение кнопок понятно из их заголовка (свойства Caption). Дадим им следующие имена (свойство Name): «Новый» — ButtonNew, «Открыть» —ButtonOpen, «Шрифт» — ButtonFont, «Выход» — ButtonExit, Memo1MemoOut.

Отметим, что кнопки «Открыть» и «Шрифт» заработают во второй части этого урока. Сейчас мы просто создаём общий интерфейс.

9. Добавьте из категории Standart в контейнер Panel компонент CheckBox  (переключатель). Он будет выполнять функцию быстрого изменения атрибута шрифта «Жирный». Переименуйте его (измените свойство Name) в CheckBold (Bold переводится как жирный). Работа с этим компонентом также будет рассмотрена во втором разделе урока. Измените заголовок переключателя (свойство Caption) на «Жирный» (рисунок 4б).

10. Установите свойство AutoSize=True для панели Panel. Размер панели автоматически изменится так, чтобы вместить все находящиеся на нём компоненты (рисунок 4б).

11. Запустите приложение. У нас получился почти полноценный редактор текста. Но вот беда… Текст мы вводить можем, но ни одна кнопка не работает. Для исправления этого досадного обстоятельства необходимо написать обработку событий OnClick для каждой кнопки.

Закройте приложение.

2. Выходим из приложения

Создайте обработчик события OnClick для кнопки «Выход»

3. Очистим редактор!

1. Выберите кнопку «Новый» и создайте для неё обработчик события OnClick. В нём запишем всего один метод для компонента Memo — Clear, который очищает содержимое этого компонента:

 

procedure TForm1.ButtonNewClick (Sender: TObject);

begin

MemoOut.Clear;

end;

 

Листинг 1

2. Запустите приложение. Теперь мы имеем простейший текстовый редактор с функциями:

  • очистки рабочей области;
  • работы с многострочным текстом;
  • вставки и удаления символов;
  • выделения текста;
  • работы с буфером обмена через горячие клавиши или контекстное меню.

Закройте приложение.

4. Коротко о главном

  • Компонент Memo обладает свойствами многострочного текстового редактора.
  • Свойство Align указывает, к какой стороне окна прижать компонент.
  • Логическое свойство AutoSize определяет, будет ли у компонента автоматический размер, в зависимости от его содержимого.
  • Компонент-контейнер – это компонент, который может содержать другие компоненты. Используется для смыслового объединения компонент.
  • Окно Дерево объектов дает возможность быстрой смены контейнера для компонентов;

Второй раздел урока: Текстовый редактор, или А мы ещё файлы открывать умеем и шрифт изменять тоже…

Выполнение упражнения

Для загрузки текстовых файлов в компонент Memo и изменения параметров шрифта будем использовать стандартные диалоговые окна Windows Шрифт (рисунок 1б) и Открыть (рисунок 1в).

 а)  б)image124  в) image113
Рисунок 1. Окно приложения

1. Открываем файл

1. Откройте проект, созданный в предыдущем разделе урока № 4 — Текстовый редактор, или Наберём и очистим!

2. Из палитры компонентов, категории Dialog, поместите на форму невизуальные компоненты: FontDialog  и OpenDialog . В какое место формы мы их поместим, это неважно, потому что они видны только на этапе разработки приложения.

  • Компонент FontDialog — вызывает окно стандартного диалога выбора параметров шрифта Шрифт.
  • Компонент OpenDialog — вызывает окно стандартного диалога выбора файлов Открыть.

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

3. Создайте обработчик события OnClick для кнопки «Открыть». Он должен:

  • вызывать стандартный диалог открытия файлов;
  • загружать указанный пользователем файл в многострочный редактор (компонент Memo).

Запишите в обработчике следующий код:

procedure TForm1.ButtonOpenClick(Sender: TObject); begin // Вызываем диалог открытия OpenDialog1.Execute; end;

Листинг 1

Метод Execute служит для вызова стандартного диалога Windows у всех компонентов (для каждого компонента свой диалог), находящихся на вкладке Dialogs.

Обратите внимание на текст, находящийся после знаков «//», он по умолчанию в Delphi выделен синим цветом. Это комментарии — второй по значимости текст после кода программы. Чтобы не запутаться в написанной программе, необходимо комментировать практически каждую написанную вами строку!

4. Запустите приложение и убедитесь, что при выборе пункта «Открыть» появляется одноимённое диалоговое окно. Однако какой бы файл вы не выбрали, ничего не произойдёт. Это вполне логично — ведь в обработчике нигде не сказано, что нужно делать с выбранным файлом. Закройте приложение.

5. Для того чтобы выбранный файл загружался в многострочный редактор, допишите в обработчик следующий код (выделено жёлтым):

 

procedure TForm1.ButtonOpenClick(Sender: TObject);

begin

// Вызываем диалог открытия OpenDialog1.Execute; // Загружаем содержимое файла в компонент

MemoOut MemoOut.Lines.LoadFromFile (OpenDialog1.FileName);

end;

 

Листинг 3

Что это означает? После того как пользователь выбирает файл и нажимает кнопку «Открыть», путь и имя файла помещаются в свойство Filename компонента OpenDialog. Потом содержимое этого файла загружается в многострочный редактор. Для этого используется метод LoadFromFile класса TStrings. Для доступа к этому классу используется свойство компонента Lines.

6. Запустите приложение и откройте любой файл (для примера, в папке «Обеспечение» есть текстовый файл «Demo.txt»). Его содержимое появится в поле компонента Memo, и вы сможете его редактировать.

Закройте приложение и снова запустите. Опять выберите пункт «Открыть«, но в диалоговом окне нажмите кнопку «Отмена«. В результате появится сообщение об ошибке (рисунок 2).

image117
Рисунок 2

Здесь сказано: «Проект Editor.exe вызвал исключение класса EFOpenError с сообщением ‘Не могу открыть файл‘. Процесс остановлен. Используйте команды Step или Run для продолжения».

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

Нажмите «ОК«. При этом вы увидите текст программы (рисунок 3). Цветом будет выделено то место, в котором программа остановилась. Вы можете нажать F9, чтобы продолжить программу с этого места (при этом вы увидите сообщение об ошибке ещё раз) или Ctrl+F2 (пункт меню Run ? Program Reset), чтобы завершить программу.

image119
Рисунок 3

7. Исправим ошибку. Всё дело в том, что вы не выбрали никакого файла, в результате свойство FileName осталось пустым, и метод LoadFromFile не смог открыть файл с пустым именем. Таким образом, необходимо вставить проверку, чтобы файл загружался только в том случае, если нажата клавиша «Открыть» (и действительно: зачем что-то загружать, если пользователь нажал «Отмена«).

Сделать такую проверку очень легко. Метод Execute возвращает логическое значение True (истина), если была нажата кнопка «Открыть«, и False (ложь), если была отмена. Остаётся только проверить это значение с помощью оператора условного перехода:

 

procedure TForm1.Button2Click(Sender: TObject);

begin

// Вызываем диалог открытия и проверяем условие нажатия на кнопку «ОK»

If OpenDialog1.Execute then

//Загружаем содержимое файла в компонент

MemoOut Memo1.Lines.LoadFromFile( OpenDialog1.FileName );

end;

 

Листинг 4

Обратите внимание, что в условии написано OpenDialog1.Execute, вместо OpenDialog1.Execute=True. Эти две записи полностью идентичны, но первая более простая и рекомендуется использовать именно её.

8. Запустите приложение и убедитесь, что теперь всё работает корректно.

Заметьте, что при вызове диалога открытия появляется список всех файлов, которые есть в выбранном каталоге. Но нам не нужен весь список. В приложениях типа Word, Photoshop и многих других в нижней части диалога открытия файлов есть выпадающий список «Тип файлов» (рисунок 4а). С его помощью можно выбрать только те типы файлов, которые будут отображаться в окне.

9. Для того чтобы пользователь мог редактировать фильтр диалога открытия файлов, у компонента OpenDialog есть свойство Filter. Откройте редактор фильтра, нажав на кнопку с тремя точками (рисунок 4б). В левой его части, построчно, записываются имена фильтров, которые будут отображаться в поле «Тип файлов», а в правой непосредственно сам фильтр (правила задания фильтрации стандартные для Windows)

а) image121б)image123
Рисунок 4. Фильтр выбора файлов

10. Заполните три строки фильтрами, как показано на рисунке 4б. При выборе первого поля диалог открытия файлов отобразит только текстовые файлы с расширением txt. Второе поле отображает файлы с расширением pas, и третье отобразит все файлы.

 

2. Форматируем текст

1. И, наконец, оживим инструменты форматирования текста (кнопка «Шрифт» и переключатель «Жирный»).

image124
Рисунок 5. Диалог выбора параметров шрифта

При нажатии на кнопку «Шрифт» должны происходить следующие действия:

  • все параметры шрифта (название, размер, цвет, стиль и пр.) компонента MemoOut передаются компоненту FontDialog. При этом в стандартном диаловом окне Windows Шрифт автоматически выставляются требуемые параметры;
  • открывается стандартный диалог шрифта (рисунок 5);
  • проверка, что пользователь нажал на кнопку «ОK», чтобы программе не прогонять оставшиеся два пункта вхолостую;
  • передача компоненту MemoOut параметров шрифта из компонента FontDialog;
  • изменение, если необходимо, состояния переключателя CheckBold.

Создайте обработчик события OnClick для кнопки «Шрифт» и запишите в нём следующий код:

 

procedure TForm1.ButtonFontClick(Sender: TObject);

begin

// Передаём компоненту FontDialog параметры шрифта из компонента

Memo FontDialog1.Font := MemoOut.Font;

// Открываем диалог шрифта 

If FontDialog1.Execute then begin

// Передаём компоненту Memo параметры шрифта из компонента

FontDialog MemoOut.Font := FontDialog1.Font;

// Изменяем, если необходимо, состояния переключателя

CheckBold.Checked := fsbold in FontDialog1.Font.Style;

end;

end;

 

Листинг 5

Поясним код:

Замечательной особенностью объектно-ориентированного программирования является возможность присвоения одинаковых значений одного компонента другому, если они принадлежат одному классу. Например, свойство Font (размер шрифта, цвет и пр.) класса TFont присутствует в объектах Memo и FontDialog, поэтому достаточно написать: FontDialog1.Font:=MemoOut.Font, чтобы передать все параметры свойства Font от объекта Memo объекту FontDialog.

Отметим особенность установки состояния переключателя «жирный»: fsbold in FontDialog1.Font.Style. Дело в том, что свойство Style у класса Font имеет тип множество. И данная конструкция означает проверку: входит ли значение fsbold в множество значений Style, и если входит, то возвращает значение True, иначе False.

2. Теперь запустите приложение и порадуйтесь, как легко изменять параметры шрифта с помощью стандартного диалога, специально для этого предназначенного.

3. Переключатель CheckBox с именем CheckBold на панели Panel предназначен для более быстрой установки жирного шрифта. Создайте обработчик события OnClick для переключателя и запишите в нём следующий код:

procedure TForm1.CheckBoldClick(Sender: TObject);
begin

  // Проверка состояния переключателя CheckBold
                       // Если включен, то текст в MemoOut делаем жирным
  If CheckBold.Checked then MemoOut.Font.Style := MemoOut.Font.Style + [fsBold] 
                       // Если выключен, то текст в MemoOut делаем светлым
                       else MemoOut.Font.Style := MemoOut.Font.Style — [fsBold]  

end;

 

Листинг 6

Изменение стиля шрифта через компоненты CheckBox осуществляется через всё то же свойство Font.Style. Синтаксис записи соответствует работе с множествами.

4. Теперь в последний раз запустите приложение. Всё работает? Отлично!

3. Коротко о главном

  • Для открытия диалога всех компонентов, расположенных в категории Dialogs палитры компонентов, служит метод Execute.
  • Метод Execute возвращает значение логического типа, определяющее результат работы с диалоговым окном.
  • Стиль шрифта (Style) задаётся через множество.
  • В случае появления сообщения об ошибке, клавишей F9 можно продолжить работу приложения.
  • В диалоге открытия файлов можно задавать любое количество фильтров для типов файлов, по правилам Windows.

Выполнение заданий

Особенности задания

  • Для полей ввода Edit необходимо ввести поясняющие надписи с помощью компонента Label.
  • Компоненты должны иметь осмысленные имена.
  • Кнопка выхода из приложения — обязательна!
  • Комментируйте код программы!
  • Перед размещением компонентов на форме желательно нарисовать их расположение на листе бумаги.

Задания I уровня сложности

1. Добавить к созданному текстовому редактору функцию сохранения текстового файла через компонент SaveDialog, категории Dialog. Для этого добавьте новую кнопку «Сохранить» рядом с кнопкой «Открыть».
Подсказка! Сохранение текста осуществляется вызовом метода SaveFromFile свойства Lines компонента Memo.


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


3. Добавить к созданному текстовому редактору функцию выбора цвета фона у компонента Memo через компонент ColorBox.


4. Добавить к созданному текстовому редактору функцию выбора цвета шрифта у компонента Memo через компонент ColorBox.


5. Добавить к созданному текстовому редактору функцию задания размера шрифта. Число, определяющее размер шрифта в пунктах, задаётся в однострочном редакторе. Применение размера осуществляется после нажатия на кнопку «Изменить», находящуюся рядом с однострочным редактором.


6. Создайте приложение, которое должно уметь открывать текстовые файлы и отображать их в многострочном редакторе. Сразу после открытия файл должен сохраниться в том же каталоге и с тем же именем, но с расширением bac.
Подсказка! Сохранение текста осуществляется вызовом метода SaveFromFile свойства Lines, компонента Memo.
Для замены расширения необходимо воспользоваться процедурами и функциями по работе со строками (см. учебное пособие по Pascal).


7. Добавить к созданному текстовому редактору функцию «помещать выбранный фрагмент текста в буфер обмена и вставлять содержимое буфера в многострочный редактор». Эти операции реализуются через две дополнительные кнопки. Также должна быть третья кнопка для автоматического выделения всего текста (аналог комбинации клавиш Ctrl+A). Для дополнительной информации смотрите свойства и методы компонента MemoOut. Обратите внимание, что по умолчанию выделение текста в многострочном редакторе не видно, если он не имеет фокуса ввода.


8. Добавить к созданному текстовому редактору функцию перемещения выделенного пользователем текста в конец, в виде новой строки. Операция должна осуществляться по нажатию на дополнительную кнопку «Переместить». Для дополнительной информации смотрите свойства и методы компонента Memo.


9. Создайте приложение, содержащее один многострочный редактор, один однострочный редактор, три компонента типа CheckBox и одну кнопку. При нажатии на кнопку в многострочном редакторе по строкам должно выводиться: содержимое однострочного редактора, состояние первого переключателя, состояние второго переключателя и состояние третьего переключателя. Для дополнительной информации смотрите свойства и методы компонента Memo.


10. Создайте приложение с многострочным редактором и кнопкой «Загрузить», при нажатии на которую появляется диалог выбора текстовых файлов. После выбора содержимое этого файла появляется в компоненте Memo. Если файл называется «demo» (в любом регистре и без учёта расширения), то содержимое будет отображаться жирным, размером 12 пунктов, тёмно-серого цвета шрифтом «Arial». Во всех остальных случаях содержимое будет отображаться обычным шрифтом чёрного цвета, размером 10 пунктов, гарнитура «Cornier New».
Для поиска названия файла необходимо воспользоваться процедурами и функциями по работе со строками (см. учебное пособие по Pascal).

Задания II уровня сложности

1. Добавить к созданному текстовому редактору функцию замены одной строки символов на другую во всём тексте. На панель необходимо добавить два однострочных редактора. В первом будет содержаться текст, который необходимо заменить, а во втором — текст, на который заменить. Замена осуществляется при нажатии кнопки «Заменить».


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


3. Создайте приложение с тремя компонентами Memo и кнопками: «Открыть 1», «Открыть 2», «Смешать» и «Сохранить». Кнопка «Открыть 1» загружает текстовый документ (через соответствующий диалог) в первый компонент Memo, кнопка «Открыть 2» — во второй, а при нажатии на кнопку «Смешать» в третьем Memo отображается текст обоих многострочных редакторов, но текст там будет «смешанным», т. е. вначале отображается первая строка первого Memo, потом первая строка второго Memo, вторая строка первого Memo, вторая строка второго Memo и т. д. Получившийся результат можно сохранить, нажав на кнопку «Сохранить».
Обратите внимание, что внутреннее представление свойства Lines — это одномерный массив строк.