КОМПЬЮТЕРНЫЕ КУРСЫ "ПОИСК"
[Главная страница] [Delphi] [Контакты]
Первым делом мы должны построить базовый интерфейс пользователя приложения. Для этого добавьте в окно Designer Surface компоненты TMainMenu, TMemo, TActionList, TOpenDialog, TSaveDialog, TStatusBar (категория Win32) и компонент TToolbar.
Форме дайте имя MainForm.
После этого выберите компонент TMainMenu и создайте группы меню Файл, Правка и Формат. Не создавайте пока что никаких пунктов меню, поскольку для реализации команд меню будут использоваться действия.
Теперь выберите компонент ТМеmо и выполните следующие шаги:
Меню File
Меню Файл должно содержать команды, которые позволят пользователю завершать работу приложения, создавать, загружать и сохранять файлы.
Пока мы не будем заниматься реализацией команд Параметры страницы и Печать.
Чтобы создать команды меню Файл, и дважды щелкните на TActionList, чтобы отобразить окно редактора Action List (Список действий).
Во-первых, нам необходимо создать команду Файл - Выход. Щелкните на кнопке New Action (Новое действие), чтобы создать новое действие, и выполните следующие шаги:
Чтобы закрыть приложение, достаточно вызвать метод Close формы в обработчике события OnExecute действия:
procedure TMainForm.ExitActionExecute(Sender: TObject); begin Close; end;
Загрузка документов
Теперь необходимо реализовать команду Файл - Открыть, чтобы пользователь мог открывать существующие файлы. Во-первых, необходимо объявить приватную переменную, которая будет хранить имя файла открытого документа.
Эта переменная необходима в команде Файл - Сохранить, а также в некоторых других местах, о чем будет сказано чуть позже.
private { Private declarations } FOpenedFile: string; public { Public declarations } end;
Чтобы пользователь мог выбрать существующий документ, вы должны использовать компонент TOpenDialog. Измените свойство Filter, чтобы диалоговое окно показывало только файлы с простым текстом, имеющие расширение .txt.
Будет разумным всегда добавлять фильтр All File Types,
поскольку пользователь может иметь документы с простым текстом,
сохраненные с расширением, отличным от .txt.
Теперь добавьте новое действие в список действий и измените его
настройки следующим образом:
Чтобы загрузить текстовый документ в компонент ТМеmо. вы должны сделать несколько вещей. Во-первых, вы должны вызвать метод Execute компонента TOpenDialog, чтобы пользователь мог выбрать документ. Если пользователь щелкнул на кнопке ОК в диалоговом окне, имя выбранного файла будет записано в свойство FileName компонента TOpenDialog, и метод Execute вернет значение True.
Когда метод Execute возвращает True, вы должны скопировать имя выбранного файла в переменную FOpenedFile. а затем вызвать метод LoadFromFile, чтобы загрузить выбранный текстовый документ в свойство Lines компонента ТМеmо. Опять-таки поместите этот код в обработчик события OnExecute действия.
procedure TMainForm.OpenActionExecute(Sender: TObject); begin if CloseCurrentDocument and OpenDialog1.Execute then begin // Сохранение имени выбранного файла FOpenedFile := OpenDialog1.FileName; // Загрузка выбранного документа Memo1.Lines.LoadFromFile(FOpenedFile); end; end;
И последнее что потребуется сделать – это создать команду Открыть в меню Файл и назначить действие OpenAction свойству Action нового меню.
Сохранение документов
Большинство приложений имеют две разные команды, которые позволяют пользователю сохранить документ: Сохранить и Сохранить как. Команда Сохранить используется для сохранения изменений в существующем документе, а команда Сохранить как — для сохранения документа, который не существует на диске или для сохранения копии существующего документа под другим именем.
Для начала мы реализуем команду Файл - Сохранить как. Команда Сохранить как должна позволить выбрать каталог и имя файла для документа с помощью компонента TSaveDialog. Измените свойства TSaveDialog:
После того как вы включите свойство ofOverwritePrompt в набор Options, компонент TSaveDialog автоматически сгенерирует предупреждающее сообщение, если пользователь попытается использовать имя файла, который уже используется.
Теперь откройте редактор Action List и создайте действие Сохранить как:
Наконец, поместите код в обработчик событий OnExecute, a затем назначьте это действие неименованному пункту в меню Файл.
procedure TMainForm.SaveAsActionExecute(Sender: TObject); begin if SaveDialog1.Execute then begin // Запоминанание имени нового файла FOpenedFile := SaveDialog1.FileName; // Сохранение документа Memo1.Lines.SaveToFile(FOpenedFile); Memo1.Modified := False; end; end;
Действие Сохранить должно выполнить две вещи: сохранить изменения, произведенные в активном документе, если документ существует, или отобразить диалоговое окно Сохранить как, если документ не существует.
Чтобы создать действие Сохранить, выполните следующие шаги:
Ниже показан обработчик события OnExecute действия Сохранить:
procedure TMainForm.SaveActionExecute(Sender: TObject); begin // Если активный документ существует, сохраните изменения if FOpenedFile <> '' then begin Memo1.Lines.SaveToFile(FOpenedFile); Memo1.Modified := False; end else // Если документ не существует, выводится диалоговое окно Save As SaveAsAction.Execute; end;
Назначьте действие SaveAction неименованному пункту в меню Файл.
Создание новых документов
Команда Файл - Новый сама по себе очень простая, поскольку мы должны выполнить всего лишь следующие шаги:
Чтобы создать действие Новый, выполните перечисленные ниже шаги:
procedure TMainForm.NewActionExecute(Sender: TObject); begin if CloseCurrentDocument then begin Memo1.Lines.Clear; FOpenedFile := ''; end; end;
Назначьте действие NewAction неименованному пункту в меню Файл.
Защита данных пользователя
Теперь, когда работа над меню File завершена, необходимо написать еще несколько строк кода, который будет защищать содержимое активного документа.
Грамотно спроектированное приложение должно проверять, изменял ли пользователь содержимое активного документа, и спрашивать пользователя о том, желает ли он сохранить документ, прежде чем разрешать пользователю:
Например, если в редакторе Блокнот вы введете некоторый текст, а затем попытаетесь закрыть редактор, то он автоматически уведомит о том, что вы не сохранили самый последний вариант изменений в документе, и позволит либо сохранить их, либо отменить изменения в активном документе.
Реализовать описанное поведение лучше всего, если создать функцию, которая будет проверять, изменял ли пользователь текст в редакторе, и спрашивать пользователя, желает ли он отменить либо сохранить изменения.
private { Private declarations } FOpenedFile: string; function CloseCurrentDocument: Boolean; public { Public declarations } end; function TMainForm.CloseCurrentDocument: Boolean; begin Result := True; {Позволяет пользователю закрыт текущий документ} if Memo1.Modified then begin case MessageDlg('Сохранить изменения в текущем документе?', mtWarning, mbYesNoCancel, 0) of mrYes: SaveAction.Execute; mrCancel:Result := False; end; // Завершение конструкции case end; // Завершение условия if Editor.Modified end;
Если текст, отображенный в редакторе, не был изменен, функция возвращает True, в результате чего мы можем либо закрыть все приложение, либо заменить текст в редакторе пустым или существующим документом. Если текст в редакторе был изменен, вызывается функция MessageDlg, чтобы пользователь мог сохранить или отменить текст, отображенный в редакторе. Если пользователь щелкает на кнопке Yes (Да), функция сначала инициирует действие Сохранить для сохранения документа и возвращает True, чтобы уведомить о том, что теперь документ можно закрыть. Если пользователь щелкает на кнопке No (Нет), функция возвращает True, поскольку пользователь желает отменить изменения. Наконец, если пользователь щелкает на кнопке Cancel (Отмена), функция возвращает False, чтобы уведомить о том, что нельзя ни закрывать приложение, ни заменять активный документ другим.
Теперь необходимо вызвать функцию CloseCurrentDocument в действиях Новый и Открыть, чтобы спросить пользователя о дальнейших действиях, если в текст были внесены изменения.
procedure TMainForm.NewActionExecute(Sender: TObject); begin if CloseCurrentDocument then begin Memo1.Lines.Clear; FOpenedFile := ''; end; end; procedure TMainForm.OpenActionExecute(Sender: TObject); begin if CloseCurrentDocument and OpenDialog1.Execute then begin // Сохранение имени выбранного файла FOpenedFile := OpenDialog1.FileName; // Загрузка выбранного документа Memo1.Lines.LoadFromFile(FOpenedFile); end; end;
Последнее, что потребуется делать — это написать обработчик для события OnCloseQuery формы.
Это событие возникает тогда, когда пользователь пытается закрыть форму. Параметр CanClose события OnCloseQuery позволяет определить, может ли форма быть закрыта или нет.
Обработчик события OnCloseQuery на самом деле очень прост. Мы должны всего лишь присвоить результат выполнения функции CloseCurrentDocument параметру CanClose. Таким образом, если функция CloseCurrentDocument вернет значение True, пользователь сможет закрыть приложение. Закрытие формы будет остановлено только лишь в том случае, если текст в редакторе будет изменен, а пользователь щелкнет на кнопке Cancel (Отмена) в диалоговом окне MessageDlg.
procedure TMainForm.FormCloseQuery(Sender: TObject; var CanClose: Boolean); begin CanClose := CloseCurrentDocument; end;
Меню Edit
Все команды меню Edit будем создавать вручную.
Отмена
Для начали создадим действие Отменить:
Чтобы отменить действия в редакторе, вы должны вызвать метод Undo. Чтобы узнать, можете ли вы отменить какое-либо действие, необходимо вызвать метод CanUndo. Но лучше всего вызвать метод CanUndo в событии OnUpdate действия, чтобы запретить действие в случае невозможности выполнения отмены.
procedure TMainForm.UndoActionExecute(Sender: TObject); begin Memo1.Undo; end; procedure TMainForm.UndoActionUpdate(Sender: TObject); begin UndoAction.Enabled := Memo1.CanUndo; end;
Назначьте действие UndoAction неименованному пункту в меню Правка.
Вырезание и копирование в буфер обмена
Действия Вырезать и Копировать подобны друг другу. Действие Копировать копирует выделенный текст в буфер обмена, а действие Вырезать сначала копирует выделенный текст в буфер обмена, а затем удаляет текст из редактора.
Поскольку действия Вырезать и Копировать имеют один обработчик события OnUpdate, давайте сначала создадим оба действия, а затем напишем необходимые обработчики событий.
Чтобы создать действие Вырезать, выполните следующие шаги:
Чтобы создать действие Копировать, выполните перечисленные ниже шаги:
Реализовать оба обработчика события OnExecute не составляет особого труда. Чтобы скопировать текст из редактора в буфер обмена, вы должны вызвать метод CopyToClipboard. Чтобы вырезать текст в буфер обмена, вызовите метод CutToClipboard редактора.
procedure TMainForm.CopyActionExecute(Sender: TObject); begin Memo1.CopyToClipboard; end; procedure TMainForm.CutActionExecute(Sender: TObject); begin Memo1.CutToClipboard; end;
Код в обработчике события OnUpdate должен запрещать действие, если в редакторе не будет выделен текст. Для определения, сколько символов было выделено пользователем, можно использовать свойство SelLength редактора. Если значение этого свойства будет равно 0, это значит, что в редакторе нет выделенных символов, и действие должно быть запрещено.
Теперь запишем следующий обработчик события OnUpdate для действия вырезать и назначим его событию OnUpdate действия Копировать:
procedure TMainForm.CutActionUpdate(Sender: TObject); begin TAction(Sender).Enabled := Memo1.SelLength > 0; end;
Назначьте действия CutAction и CopyAction неименованным пунктам в меню Правка.
Вставка в буфер обмена
Во время действия Вставить содержимое буфера обмена вставляется в редактор. Чтобы ваш редактор был по-настоящему профессиональным, вы должны позволить пользователю выбирать команду Вставить только тогда, когда в буфере обмена будет содержаться хоть какой-нибудь текст. Чтобы узнать, какой формат имеют данные, хранящиеся в буфере обмена, вызовите метод HasFormat.
Чтобы узнать, содержит ли буфер обмена простой текст, который может быть вставлен в редактор, вызовите метод HasFormat и передайте в качестве параметра константу CF_TEXT. Чтобы использовать глобальный объект Clipboard, необходимо добавить модуль Clipbrd в список uses.
Для создания действия Вставить потребуется выполнить следующие шаги:
procedure TMainForm.PasteActionExecute(Sender: TObject); begin Memo1.PasteFromClipboard; end; procedure TMainForm.PasteActionUpdate(Sender: TObject); begin PasteAction.Enabled := Clipboard.HasFormat(CF_TEXT); end;
Назначьте действие PasteAction неименованному пункту в меню Правка.
Удаление
Действие Удалить выполняет практически то же самое, что и действие Вырезать. Подобно Вырезать, действие Удалить удаляет выделенный текст из редактора. Различие между ними состоит в том, что действие Удалить не копирует текст в буфер обмена перед удалением текста из редактора.
Чтобы создать действие Delete, выполните следующие шаги:
Для удаления текста из редактора без изменения содержимого буфера обмена применяется метод ClearSelection. Кроме этого, назначьте обработчик события OnUpdate действия Cut событию OnUpdate действия Delete, чтобы позволить пользователю удалять выделенный текст только в том случае, если этот текст существует.
Назначьте действие DeleteAction неименованному пункту в меню Правка.
Выделение всего документа
Пожалуй, наиболее простым из всех действий является Выделить все, которое позволяет выделять все содержимое редактора. Чтобы создать действие Выделить все, выполните следующие шаги:
Назначьте действие SelectAllAction неименованному пункту в меню Правка.
Чтобы выделить все содержимое компонента ТМеmо, необходимо вызвать метод SelectAll:
procedure TMainForm.SelectAllActionExecute(Sender: TObject); begin Memo1.SelectAll; end;
Поиск текста в Delphi
Чтобы реализовать команду Найти, сначала добавьте в окно Designer Surface компонент TFindDialog. Компонент TFindDialog заключает в себе обычное диалоговое окно Найти, с помощью которого пользователь может производить поиск строки текста. Строка, которую пользователь пытается найти, хранится в свойстве FindText.
Чтобы пользователь мог осуществлять поиск в тексте, потребуется создать действие Найти, отображающее диалоговое окно Найти, и написать обработчик для события OnFind диалогового окна для реализации поиска. Поскольку диалоговое окно Найти позволяет искать несколько экземпляров строки, мы должны объявить переменную Integer, в которой будет храниться последняя позиция поиска:
private { Private declarations } FLastSearch: Integer; // последняя позиция поиска FOpenedFile: string; function CloseCurrentDocument: Boolean; public { Public declarations } end;
Теперь, чтобы создать действие Найти, выполните перечисленные ниже шаги:
procedure TMainForm.FindActionExecute(Sender: TObject); begin // Старт поиска в начале документа FLastSearch := 0; // Отображение диалогового окна FindDialog1.Execute; end;
Код, отвечающий за выполнение поиска:
procedure TMainForm.FindDialog1Find(Sender: TObject); var memoText: string; searchPos: Integer; dialog: TFindDialog; begin dialog := TFindDialog(Sender); memoText := Memo1.Lines.Text; if FLastSearch <> 0 then Delete(memoText, 1, FLastSearch); searchPos := Pos(dialog.FindText, memoText); if searchPos = 0 then MessageDlg(Format('Не могу найти "%s"', [dialog.FindText]), mtInformation, [mbOK], 0) else begin Inc(FLastSearch, searchPos); Memo1.SelStart := Pred(FLastSearch); Memo1.SelLength := Length(dialog.FindText); Memo1.SetFocus; end; end;
Первая строка (приведение параметра Sender к типу TFindDialog) необязательна, если в команде Правка - Найти вы намереваетесь использовать только этот метод. В данном случае мы должны выполнить приведение к типу по той причине, что это позволит повторно использовать этот метод в событии OnFind компонента TReplaceDialog.
В следующих двух строках создается временная копия всего документа и удаляется та часть документа, в которой пользователь уже выполнил поиск. Если мы не удалим эту часть, функция Pos, используемая для выполнения поиска, всегда будет возвращать первый экземпляр строки поиска.
Если функция Pos найдет экземпляр строки поиска, мы должны будем сохранить позицию найденного экземпляра в переменной FLastSearch, а затем выделить строку в редакторе. Для выделения строки в редакторе потребуется изменить свойства SelStart и SelLength. Свойство SelStart показывает позицию курсора, а свойство SelLength определяет количество выделенных символов.
Наконец, чтобы показать выделение, мы должны вызвать метод SetFocus для передачи редактору фокуса.
Назначьте действие FindAction неименованному пункту в меню Правка.
Замена текста
В плане завершения работы над меню Правка необходимо создать действие Заменить, которое позволит производить поиск и замену строки. Чтобы реализовать действие Заменить, потребуется выполнить следующие шаги:
Для создание действия Заменить потребуется выполнить перечисленные ниже шаги:
Мы должны изменить свойство Tag с целью повторного использования обработчика события OnExecute действия Найти. После того как вы измените свойство Tag действия Заменить, вы можете изменить обработчик события OnExecute действия Найти, чтобы отобразить оба диалоговых окна:
procedure TMainForm.FindActionExecute(Sender: TObject); begin // Старт поиска в начале документа FLastSearch := 0; if TComponent(Sender).Tag = 0 then // Отображение диалогового окна FindDialog1.Execute else ReplaceDialog1.Execute; end;
Наконец, мы должны реализовать обработчики для события OnFind. которое происходит при щелчке пользователем на кнопке Найти, и события OnReplace, которое происходит при щелчке пользователем на кнопках Заменить или Заменить все.
На самом деле, писать обработчик для события OnFind не потребуется, поскольку можно воспользоваться существующим обработчиком события OnFind компонента TFindDialog. Этот обработчик можно использовать по той причине, что вначале было выполнено приведение параметра Sender к компоненту TFindDialog, а также потому, что TReplaceDialog происходит от класса TFindDialog.
В обработчике события OnReplace необходимо проверить, существует ли выделенный текст в редакторе (SelText <> ''), и если он существует, то заменить его строкой из свойства ReplасеТехt.
procedure TMainForm.ReplaceDialog1Replace(Sender: TObject); begin if Memo1.SelText <> '' then Memo1.SelText := ReplaceDialog1.ReplaceText; end;
Меню Format
Меню Формат в нашем примере приложения является наиболее простым меню. Оно состоит только из двух команд — Перенос по словам и Шрифт.
Команда Перенос по словам позволяет переносить текст по правому краю. Если установить галочку напротив этой команды, то вы сможете прокручивать содержимое редактора по вертикали. Если галочка не будет установлена, вы должны будете отображать обе линейки прокрутки, поскольку длинные строки не будут переноситься по правому краю.
Чтобы создать действие Перенос по словам, выполните следующие шаги:
И, наконец, напишите следующий обработчик события OnExecute:
procedure TMainForm.WordWrapActionExecute(Sender: TObject); const SCROLLS: array[Boolean] of TScrollStyle = (ssBoth, ssVertical); begin Memo1.WordWrap := WordWrapAction.Checked; Memo1.ScrollBars := SCROLLS[Memo1.WordWrap]; end;
Назначьте действие WordWrapAction неименованному пункту в меню Формат.
Чтобы создать команду Формат - Шрифт, сначала добавьте в окно Designer Surface компонент TFontDialog, а затем создайте новое действие. В поле Caption нового действия введите Шрифт, присвойте действию имя FontAction, a затем напишите следующий код в обработчике события OnExecute:
procedure TMainForm.FontActionExecute(Sender: TObject); begin // Отображение текущего шрифта в диалоговом окне FontDialog1.Font.Assign(Memo1.Font); // Изменение шрифта if FontDialog1.Execute then Memo1.Font.Assign(FontDialog1.Font); end;
Назначьте действие FontAction неименованному пункту в меню Формат.
Отображение подсказок и состояния
Добавьте на форму компонент TStatusBar. Если свойству SimplePanel присвоить значение True, то компонент TStatusBar сможет отображать только одну порцию информации, заданную свойством SimpleText. Однако если свойству SimplePanel присвоить значение False, мы сможем использовать свойство Panels для создания многопанельной строки состояния и отображения большего количества информации для пользователя.
Выберите компонент строки состояния и щелкните на кнопке (...) рядом со свойством Panels, чтобы вывести на экран редактор коллекций Collection Editor.
Щелкните на кнопке Add New (Добавить новую), чтобы создать три панели строки состояния. Присвойте свойству Width первых двух панелей значение 75. чтобы зарезервировать большее пространство для вывода текста.
Первая панель будет служить для отображения количества строк в документе и номера выбранной в данный момент отроки. Вторая панель будет использоваться для отображении идентификатора Modified (Изменено), если содержимое редактора будет изменяться, а последняя панель будет применяться для вывода подсказок.
Наилучшим местом для этого кода является событие OnIdle приложения, поэтому добавьте в окно Designer Surface компонент TApplicationEvents и в обработчик события OnIdle поместите следующий код:
procedure TMainForm.ApplicationEvents1Idle(Sender: TObject; var Done: Boolean); const MODIFIED: array[Boolean] of string = ('', 'Modified'); begin // Состояние строки StatusBar1.Panels[0].Text := Format('Линия %d/%d', [Succ(Memo1.CaretPos.Y), Succ(Memo1.Lines.Count)]); // Измененное состояние StatusBar1.Panels[1].Text := MODIFIED[Memo1.Modified]; end;
Чтобы отобразить подсказку в последней панели, подготовьте обработчик для события OnHint:
procedure TMainForm.ApplicationEvents1Hint(Sender: TObject); begin StatusBar1.Panels[2].Text := Application.Hint; end;
Панель инструментов
Теперь нам осталось сделать всего две вещи: добавить глифы для наиболее часто используемых действий, а затем поместить эти действия в панель инструментов. Для начала добавьте в окно Designer Surface компонент TImageList, присвойте ему имя Normal, а затем добавьте глифы для действий New, Open, Save, Undo, Cut, Copy, Paste, Delete и Find. После того как вы добавите эти изображения в компонент TImageList, назначьте компонент TImageList компонентам TActionList, TToolbar и TMainMenu, после чего откройте редактор Action List и назначьте глифы соответствующим действиям.
После того как вы назначите глифы действиям, выберите панель инструментов и щелкните на ней правой кнопкой мыши, чтобы вывести контекстное меню панели инструментов. Контекстное меню содержит команду New Button (Новая кнопка) для создания новой кнопки панели инструментов, а также команду New Separator (Новый разделитель) для создания разделителя, который позволит визуально распределить кнопки по группам. Все, что вам необходимо сделать сейчас — это добавить несколько кнопок в панель инструментов и назначить действие свойству Action каждой кнопки.
При желании можете также изменить следующие два свойства, чтобы придать панели инструментов более привлекательный вид:
Исходный код здесь. Выполнен на Delphi XE.
Источник: Иван Хладни - Внутренний мир Borland Delphi 2006.