Добавляем компонент в стандартный message dialog

Содержание

Урок 21. Диалог для открытия файла

Диалог для открытия файла. Для добавления в программу стандартного диалога для открытия файла добавьте на форму компонент OpenDialog с вкладки Палитры компонентов.

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

Для того чтобы при нажатии на кнопку диалоговое окно появилось, добавьте в ее обработчик, следующий код:

procedure TForm1.Button1Click(Sender: TObject);

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

if OpenDialog1.Execute then

Таким образом для извлечения файла мы используем свойство FileName нашего диалога. Обратите внимание, что с помощью if мы проверяем, выбрал ли пользователь вообще что-нибудь. Если пользователь ничего не выбрал, то OpenDialog1.Execute возвращает false , и последующий код не выполняется. Вот еще несколько полезных свойств для нашего диалога для открытия файлов.

Для открытия в диалоге заранее определенной папки используем свойство InitialDir :

procedure TForm1.Button1Click(Sender: TObject);
begin
OpenDialog1.InitialDir:=’D:\’;
if OpenDialog1.Execute then
Memo1.Lines.LoadFromFile(OpenDialog1.FileName);
end;

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

Затем просто заполните левый и правый столбцы. Левый определяет, что за надпись увидит пользователь, а правый — что за файлы будут показываться.

Обратите внимание, что мы можем для одного описания задать несколько масок, разделяя их точкой с запятой.

Свойство FilterIndex диалогового окна определяет номер активного фильтра (т. е. того, который будет выбран при вызове диалога). Нумерация при этом начинается с единицы.

А вот так можно задать несколько фильтров в тексте программы:

Полный список

— создаем AlertDialog
— настраиваем заголовок, сообщение, картинку и кнопки

Начнем знакомство с AlertDialog. Этот диалог используется, если вы хотите сообщить о чем-то пользователю или попросить его сделать выбор типа Да/Нет/Отмена.

Напишем приложение, которое при закрытии будет вызывать диалог о сохранении данных, аналогичный диалогу из программ MS Office . Если мы ответим Да, то данные сохранятся, если Нет – то не сохранятся, если Отмена – приложение не закроется.

Project name: P0601_AlertDialogSimple
Build Target: Android 2.3.3
Application name: AlertDialogSimple
Package name: ru.startandroid.develop.p0601alertdialogsimple
Create Activity: MainActivity

Добавим в res/values/strings.xml строки c текстами:

В обработчике кнопки onclick вызываем диалог.

В onCreateDialog мы создаем диалог. Для этого используется класс AlertDialog.Builder. Мы указываем заголовок, текст сообщения, иконку и кнопки. Диалог может содержать максимум три кнопки ответа: положительная, отрицательная и нейтральная. Для каждой указываем текст и обработчик. Метод create создает диалог и мы его возвращаем (return).

Обработчик кнопок myClickListener реализует интерфейс DialogInterface.OnClickListener и в нашем случае является общим для всех кнопок. В нем мы проверяем, какая кнопка была нажата:
если положительная (BUTTON_POSITIVE), то сохраняем данные и закрываем приложение
если отрицательная (BUTTON_NEGATIVE), то закрываем приложение без сохранения
если нейтральная (BUTTON_NEUTRAL), то не делаем ничего

В своем методе saveData выводим текст, что данные как-будто сохранены. Просто, чтобы убедиться, что метод выполняется.

Все сохраним и запустим приложение. Нажмем кнопку Выход:

то приложение закроется и метод saveData будет выполнен.

Если жмем Отмена, то диалог закроется и с приложением ничего не произойдет.
А если жмем Нет, то приложение закроется без вызова нашего метода saveData.

Вот так несложно и недолго создать диалог для взаимодействия с пользователем. Использованные нами атрибуты диалога не являются обязательными. Вы можете, например, не указывать заголовок, или сделать только одну кнопку, а не три.

Для указания заголовка, сообщения и текстов кнопок необязательно использовать переменные R.string. Есть аналогичные реализации методов, которые принимают на вход просто текст.

И еще пара советов.

1) Чтобы диалог вызывался не только по кнопке выход, но и при нажатии на кнопку Назад в приложении, добавьте вызов диалога в реализацию метода onBackPressed

2) А если хотите, чтобы вызванный диалог не закрывался по нажатию кнопки Назад, то используйте метод setCancelable:

На следующем уроке:

— используем метод подготовки диалога

Присоединяйтесь к нам в Telegram:

— в канале StartAndroid публикуются ссылки на новые статьи с сайта startandroid.ru и интересные материалы с хабра, medium.com и т.п.

— в чатах решаем возникающие вопросы и проблемы по различным темам: Android, Kotlin, RxJava, Dagger, Тестирование

— ну и если просто хочется поговорить с коллегами по разработке, то есть чат Флудильня

— новый чат Performance для обсуждения проблем производительности и для ваших пожеланий по содержанию курса по этой теме

Диалоговые окна

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

Дизайн диалогового окна

Подробную информацию о дизайне диалоговых окон, включая рекомендации для вашего языка см. в Руководстве по дизайну диалоговых окон.

Класс Dialog — это базовый класс для создания диалоговых окон, но реализовывать напрямую класс Dialog не рекомендуется. Вместо этого следует использовать один из следующих подклассов:

AlertDialog Диалоговое окно, в котором могут отображаться заголовок, кнопки вплоть до трех штук, список из выбираемых элементов либо пользовательский макет. DatePickerDialog или TimePickerDialog Диалоговое окно с предопределенным пользовательским интерфейсом, с помощью которого пользователь указывает значения даты или времени.

Следует избегать ProgressDialog

В Android имеется другой класс диалоговых окон под названием ProgressDialog , которые отображают диалоговое окно с индикатором выполнения процесса. Тем не менее, если необходимо отобразить прогресс загрузки или неопределенный процес, рекомендуется следовать инструкциям по дизайну Выполнение & Операции, чтобы использовать ProgressBar в вашем макете.

Эти классы определяют стиль и структуру вашего диалогового окна, однако следует использовать DialogFragment в качестве контейнера вашего диалогового окна. Класс DialogFragment предоставляет все функции, необходимые для создания диалогового окна и управления его внешним видом, вместо вызова методов к объекту Dialog .

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

В следующих разделах руководства описано использование DialogFragment в сочетании с объектом AlertDialog . Если необходимо создать элемент выбора даты или времени, обратитесь к руководству Элементы выбора.

Примечание: Поскольку класс DialogFragment изначально включен в Android 3.0 (уровень API 11), в настоящем документе описывается использование класса DialogFragment , предоставляемого в Библиотеке поддержки. После добавления этой библиотеки в приложение появится возможность использовать DialogFragment и множество других API на устройствах, работающих на Android 1.6 и выше. Если минимальная версия вашего приложения поддерживает уровень API 11 и выше, можно использовать фреймворк-версию DialogFragment , но следует иметь в виду, что данный документ ссылается на API со вспомогательными библиотеками. При использовании вспомогательной библиотеки необходимо импортировать класс android.support.v4.app.DialogFragment , а не android.app.DialogFragment .

Создание фрагмента диалогового окна

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

Например, имеется базовый класс AlertDialog , управляемый в рамках DialogFragment :

Рисунок 1. Диалоговое окно с сообщением и двумя кнопками действия.

Итак, после создания экземпляра этого класса и вызова show() к этому объекту появляется диалоговое окно, показанное на рисунке 1.

В следующем разделе предоставлена более подробная информация об использовании API AlertDialog.Builder для создания диалоговых окон.

В зависимости от сложности создаваемого диалогового окна возможно реализовать множество других методов обратного вызова в DialogFragment , включая все базовые методы жизненных циклов фрагментов.

Создание диалогового окна оповещения

С помощью класса AlertDialog создается многообразие решений касательно внешнего вида диалогового окна , и зачастую этого класса вполне достаточно. Как показано на рис. 2, диалоговое окно состоит из трех областей:

Рисунок 2. Макет диалогового окна.

Это дополнительная возможность, которая используется только в случае, если область содержимого занята подробным сообщением, списком или пользовательским макетом. Если необходимо отобразить простое сообщение или вопрос (как, например, в диалоге на рисунке 1), заголовок не нужен.
Область содержимого

Здесь может отображаться сообщение, список или другой пользовательский макет.

В диалоговом окне не должно содержаться более трех кнопок действия.

Класс AlertDialog.Builder предоставляет API, с помощью которых можно создавать AlertDialog с этими видами содержимого, включая пользовательский макет.

В следующих главах показано, как определять различные атрибуты диалоговых окон с помощью класса AlertDialog.Builder .

Добавление кнопок

Для добавления кнопок, изображенных на рисунке 2, вызывайте методы setPositiveButton() и setNegativeButton() :

Методы set. Button() предполагают заголовок для кнопки (реализуемый через строковый ресурс) и DialogInterface.OnClickListener , который определяет действие, следующее за нажатием кнопки пользователем.

Реализована возможность добавлять три различных вида кнопок действий:

Положительные Используются для подтверждения и продолжения дейстия (кнопка «ОК»). Отрицательные Используются для отмены действия. Нейтральные Используются в случаях, когда пользователь может не желать продолжить действие, но при этом необязательно хочет его отменить. Появляется между положительными и отрицательнымиI кнопками. Примером такого действия может быть «Напомнить позже».

Можно добавлять только одну кнопку каждого вида в AlertDialog . Это означает, что нельзя использовать более одной «положительной» кнопки.

Рисунок 3. Диалоговое окно с заголовком и списком.

Добавление списка

В API AlertDialog реализована возможность использования трех видов списков:

  • Традиционный список с выбором одного варианта
  • Интерактивный список с выбором одного варианта (переключатели)
  • Интерактивный список с выбором нескольких вариантов (флажки)

Для создания списка с выбором одного варианта, как на рисунке 3, используйте метод setItems() :

Поскольку список отображается в области содержимого диалогового окна, диалоговое окно не может показать одновременно сообщение и список, поэтому необходимо задать заголовок диалогового окна с помощью setTitle() . Для указания элементов списка необходимо вызвать setItems() , передающий указатель. В качестве другого варианта можно указать список с помощью setAdapter() . Наполнение списка динамическими данными (например, из базы данных) происходит с помощью ListAdapter .

Если вы предпочтете реализовать список с помощью ListAdapter , рекомендуется использовать Loader , чтобы содержимое загружалось асинхронно. Подробно этот процесс описан далее в руководстве по Созданию макетов с помощью адаптера и загрузчиков .

Примечание: По умолчанию нажатие по элементу списка отменяет диалоговое окно, за исключением случаев, когда используется один из следующих интерактивных списков.

Рисунок 4. Список с несколькими вариантами ответов.

Добавление интерактивного списка с одним или несколькими вариантами ответов

Для добавления списка с несколькими вариантами ответов (флажки) или списка с одним вариантом ответа (переключатели) используйте методы setMultiChoiceItems() или setSingleChoiceItems() соответственно.

Например, таким образом можно создать список с несколькими вариантами ответов, как на рисунке 4, который сохраняет выбранные элементы в ArrayList :

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

Создание пользовательского макета

Рисунок 5. Пользовательский макет диалогового окна.

Если в диалоговом окне необходим пользовательский макет, нужно создать макет и добавить его в AlertDialog путем вызова setView() в объекте AlertDialog.Builder .

По умолчанию пользовательский мает заполняет окно диалога, при это все равно можно использовать методы AlertDialog.Builder для добавления кнопок и заголовка.

В качестве примера на рисунке 5 приведен файл макета для диалогового окна.

Совет. По умолчанию при настройке элемента EditText для типа ввода «textPassword» используется семейство шрифтов фиксированной ширины, поэтому необходимо изменить семейство шрифтов на «sans-serif» , чтобы в обоих текстовых полях использовались одинаковые стили шрифта.

Для применения макета в вашем DialogFragment вам понадобится LayoutInflater с getLayoutInflater() и вызов inflate() , где первым параметром будет являться ID ресурса макета, а вторым параметром — исходный вид макета. Затем можно вызвать setView() для размещения макета в диалоговом окне.

Совет. Если необходимо пользовательское диалоговое окно, можно отображать Activity в качестве диалога вместо API Dialog . Нужно создать операцию и установить тему Theme.Holo.Dialog в элементе манифеста :

Готово. Операция теперь отображается в диалоговом окне, а не в полноэкранном режиме.

Передача событий обратно в основное диалоговое приложение

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

Например, DialogFragment определяет интерфейс, по который доставляет события обратно в основной компонент операции:

Операция, выполняемая диалоговым окном, создает экземпляр диалогового окна с помощью конструктора фрагментов диалогового окна и получает события диалога с помощью реализации интерфейса NoticeDialogListener :

Поскольку выполняемая операция реализуется через NoticeDialogListener с помощью метода обратного вызова onAttach() , во фрагменте диалога могут использоваться методы обратного вызова интерфейса для доставки событий нажатий к операциям:

Отображение диалогового окна

Для отображения диалогового окна необходимо создать экземпляр DialogFragment и вызвать show() , передавая FragmentManager и наименование тега для фрагмента диалога.

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

Отображение диалогового окна в полноэкранном режиме или в виде встроенного фрагмента

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

Тем не менее, в этом случае нельзя использовать AlertDialog.Builder или другие объекты Dialog для построения диалогового окна. Если необходимо сделать DialogFragment встраиваемым, нужно определить пользовательский интерфейс диалогового окна в макете методом обратного вызова onCreateView() .

В качестве примера приведен DialogFragment , который появляется либо в виде диалогового окна, либо в виде встраиваемого фрагмента (используя макет с наименованием purchase_items.xml ):

Приведен пример кода, реализующего принятие решения об отображении фргмента в качестве диалогового окна или полноэкранного пользовательского интерфейса на основе размера экрана:

Подробные сведения о выполнении операций с фрагментами приведены в руководстве Фрагменты.

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

Затем можно инизиализировать значение mIsLargeLayout в течение выполнения метода операции onCreate() :

Отображение операции в качестве диалога на больших экранах

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

Для отображения операции в качестве диалогового окна только на больших экранах необходимо применить тему Theme.Holo.DialogWhenLarge к элементу манифеста :

Подробная информация о темах операций приведена в руководстве Стили и темы.

Закрытие диалогового окна

Когда пользователь нажимает кнопки, созданные с помощью AlertDialog.Builder , система закрывает диалоговое окно самостоятельно.

Система также закрывает диалоговое окно, когда пользователь нажимает на элемент списка в диалоговом окне, за исключением списков с переключателями или флажками. В иных случаях можно вручную закрыть диалоговое окно путем вызова dismiss() в DialogFragment .

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

Также можно отменить диалоговое окно. Это особое событие, возникающее, когда пользователь покинул диалоговое окно, не завершив задачу. Так происходит, когда пользователь нажимает кнопку Назад, касается экрана за областью диалогового окна, либо когда задано cancel() в Dialog (например, в качестве отклика на нажатие кнопки «Отмена» в диалоговом окне).

Как показано в примере выше, можно ответить на событие отмены с помощью onCancel() в классе DialogFragment .

Примечание: Система вызывает onDismiss() при каждом событии, которое вызывается методом обратного вызова onCancel() . Тем не менее, при вызове Dialog.dismiss() или DialogFragment.dismiss() , система вызывает onDismiss() , а не onCancel() . Поэтому в общих случаях вызов dismiss() производится при нажатии пользователем положительной кнопки в диалоговом окне, а после диалоговое окно закрывается.

Content and code samples on this page are subject to the licenses described in the Content License. Java is a registered trademark of Oracle and/or its affiliates.

TDelphiBlog

Блог Delphi-программиста. Обзоры инструментов и экспертов для Delphi. Описание JCL, JVCL, cnWizards. Дженерики в Delphi. Дневник разработки Lazy Delphi Builder. Переводы.

Страницы

Motto

В тихом саду здравомыслия
Пусть на вас постоянно падают
кокосовые орехи пробужденности.

четверг, 25 декабря 2008 г.

Описание диалогов в JVCL. JvDialogs. Часть 2.

В отличие от диалогов описанных в предыдущем посте, диалоги описанные здесь, выполняют более полезные функции. =)

Диалоги, построенные на Delphi

TJvDesktopAlert и TJvDesktopAlertStack

Компоненты для показа всплывающих окошек.(Popup hint window).

Илон Маск рекомендует:  Что такое код parent

Можно настраивать любые параметры, окошки появляются на заданное время и тают по истечении. При наведении мышки, они перестают пропадать. Есть возможность перетаскивать их мышкой, обрабатывать клики на разных областях, присваивать Popup menu для стрелочки. TJvDesktopAlert отвечает за прорисовку окошка. TJvDesktopAlertStack отвечает за список активных окошек. Единственный минус с которым я столкнулся при использовании этого компонента – это то, что всё работает хорошо до тех пор пока мы не попытаемся уместить на нём больше текста. Советую посмотреть демку: JVCL\examples\JvDesktopAlert.

TJvDSADialog

Компонент для создания собственных диалогов(MessageDlg) с галочкой «Не показывать больше это окно»(Don’t Show Again), а также диалогов закрывающихся по таймеру. Прежде чем его использовать обратите внимание на готовые функции предоставленные в модуле JvDSADialogs.pas. Там полно функций, позволяющих показывать диалог с заданными параметрами. Также там есть функции заменяющие стандартные ShowMessage, MessageDlg, MessageDlgEx.
Этот компонент подробно описан в JVCL Help-е. Также советую посмотреть демку JVCL\Examples\JvDSADialogs\MessageDlgEditor.dpr.

TJvTipOfDay

Диалог “Совет Дня”. Умеет автоматически показывать себя при старте программы(отключаемо), сохранять свои свойства в AppStorage[1]. Хранит советы в Tips:Tstrings. Имеет два вида отображения.

TJvFindReplace

TJvLoginDialog

Диалог запрашивающий имя пользователя и пароль. По умолчанию автоматически запускается при старте программы(свойство Active) и в случае неправильного пароля, не позволяет запуститься программе. Имеет свойства: Caption; Количество попыток ввода пароля; Максимальная длина пароля, умеет сохраняться в AppStorage[1].

TJvProgressDialog

диалог с прогрессбаром.

TJvProgressComponent

компонент, показывающий диалог с прогрессбаром в модальном или немодальном режиме.

TJvDualListDialog

TJvBrowseForFolderDialog

Диалог для выбора папки(Browse For Folder). Обёртка вокруг функции SHBrowseForFolder.

Диалоги для выбора цвета

JvColorDialog : TJvColorDialog

JvFullColorDialog : TJvFullColorDialog

JvFullColorCircleDialog : TJvFullColorCircleDialog

Из бесплатных контролов для выбора цвета, хочу упомянуть ещё Color Picker Control от Soft-Gems. Версии для D2009 там пока нет.

Устаревшие диалоги

TJvOpenDialog, TJvSaveDialog – немного расширенные диалоги для загрузки/сохранения файлов. В Висте визуально ничем не отличаются от стандартных TOpenDialog, TSaveDialog.
TJvSelectDirectory – устаревший аналог TJvBrowseForFolderDialog. Надеюсь, что его уберут из следующей версии.

Проблемные диалоги

Диалоги с которыми у меня возникли проблемы. Причины проблем в совокупности этих параметров: Windows Vista 32 bit, Delphi 2009, JVCL 3.36 =) Описанные фичи и баги актуальны для JVCL 3.35, точнее для исходников в репозитории на 24-12-2008.
TJvImageDialog – при тестовом запуске подвесил IDE.
TJvPageSetupDialog, TJvPageSetupTitledDialog, TJvAddHardwareDialog – глючат.

Добавляем компонент в стандартный Message dialog.

Пример показывает стандартное диалоговое окно, которое обычно используется для подтверждения дальнейших действий в любой программе с галочкой «Don’t show this message again.»

Используем функцию CreateMessageDialog и добавляем любой компонент до того как будет вызвана ShowModal.

procedure TForm1.Button1Click(Sender: TObject);
Var
AMsgDialog: TForm;
ACheckBox: TCheckBox;
begin
AMsgDialog := CreateMessageDialog(‘This is a test message.’, mtWarning, [mbYes, mbNo]);
ACheckBox := TCheckBox.Create(AMsgDialog);
with AMsgDialog do
try
Caption := ‘Dialog Title’ ;
Height := 169;

With ACheckBox do
begin
Parent := AMsgDialog;
Caption := ‘Do not show me again.’;
top := 121;
Left := 8;
end;

Case ShowModal of
ID_YES: ;//здесь Ваш код после того как диалог будет закрыт
ID_NO: ;
end;
If ACheckBox.Checked then
begin
//.
end;
finally
ACheckBox.Free;
Free;
end;
end;

Так же Вы можете изменить диалог по Вашему усмотрению.

Добавляем компонент в стандартный message dialog

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

На вкладке палитры компонентов Dialogs находятся компонент Delphi OpenDialog и компонент Delphi SaveDialog. Все Delphi диалоги, находящиеся на этой вкладке, в том числе и Delphi диалоги выбора файла, невизуальные, т.е. при переносе их на Форму в работающей программе их не видно, они видны только на этапе конструирования. Компонент Delphi OpenDialog позволяет открыть в нашей программе стандартное Windows-окно диалога открытия файла, компонент Delphi SaveDialog — окно диалога сохранения.

Delphi диалоги выбора файла сами по себе ничего не делают, а только предоставляют настройки, сделанные пользователем при выборе файла. Самый важный метод Delphi диалоговExecute. Он срабатывает в момент нажатия кнопки «открыть» или «сохранить» в окне выбора файла. Для примера давайте введём в программу возможность выбора файла для загрузки в редактор Memo, и сохранения после редактирования.

Итак, кидаем на Форму оба Delphi диалога, текстовый редактор Memo, и три кнопки Button. В свойство Caption одной из них записываем «Открыть. «, другой — «Сохранить«, третьей — «Сохранить как. «

В обработчике OnClick кнопки «Открыть. » пишем:

if OpenDialog1.Execute then
Memo1.Lines.LoadFromFile(OpenDialog1.FileName);

В результате выбора файла свойство FileName компонента OpenDialog получает значение полного адреса выбранного файла, который мы и вставляем в функцию загрузки файла компонента Memo.
Всё это хорошо, но только в данном случае, когда записанное выражение записывается в одну строку. Если программа использует несколько раз выражение OpenDialog1.FileName, то писать руками устанешь. В Delphi для такого случая есть так называемый «оператор присоединения» with. Он используется для любых объектов, имеющих длинный «хвост» из свойств, которые приходится записывать многократно. Вот как он записывается:

Свойства Объекта внутри логических скобок begin/end можно записывать непосредственно. Допускается перечислять через запятую несколько объектов. Естественно, в случае, когда внутри скобок находится один оператор, они необязательны. Перепишем фрагмент загрузки файла с использованием оператора присоединения:

with OpenDialog1, Memo1 do
if Execute then
Lines.LoadFromFile(FileName);

Запись получается более компактной.
Так как свойства компонентов OpenDialog и SaveDialog одинаковы, сохранение текста выглядит абсолютно аналогично. Создаём обработчик нажатия кнопки «Сохранить как. » и пишем:

with SaveDialog1, Memo1 do
if Execute then
begin
Lines.SaveToFile(FileName);
OpenDialog1.FileName:=FileName; // Чтобы исправленный текст не затёр источник
end;

Наконец, для кнопки «Сохранить» пишем:

Memo1.Lines.SaveToFile(OpenDialog1.FileName); // Сохраняем туда, откуда считали

(В предыдущей строчке была ошибка. Как справедливо заметил в комментариях Oraculum — OpenDialog1.FileNam e нужно писать без кавычек.)

При работе этих фрагментов можно заметить, что выбирать приходится из всех файлов в нужной директории. Удобнее видеть только, например, текстовые файлы, или другой тип файлов по нашему выбору. Для этого используются фильтры, свойство Filter в наших компонентах. Настраивается оно в Инспекторе Объектов. При выборе его можно перейти в редактор фильтров:

В колонке FilterName записываем имена фильтров, в колонке Filter — список масок файлов, разделённых точкой с запятой. Маска файла в данном случае выглядит как

Звёздочка означает, что выбираются файлы с любыми именами, подходящие по расширению.

Свойство Delphi диалогов Title позволяет записать в заголовок нужную нам фразу. Если оставить его пустым, то в заголовке будут стандартные «открыть» или «сохранить»
Свойство InitialDir позволяет в момент открытия оказаться в нужной нам директории. Оно доступно как на этапе «конструирования», так и программно.

Теперь, пользуясь всем пройденным материалом, можно решить задачку на тему: как определить размер файла

Добавляем компонент в стандартный message dialog

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

Диалоги работы с файлами

Все диалоги собраны на обзей закладке Dialogs палитры компонентов. Первым на ней является компонент OpenDialog, представляющий собой оболочку для функций Windows API, связанных с диалогом выбора файлов. Диалог, вызываемый при помощи компонента OpenDialog, используется для выбора (открытия) файла. Для операции сохранения следует использовать другой компонент — SaveDialog. Он показывает, в общем-то, тот же самый диалог, но в режиме сохранения.

Еще 2 компонента — OpenPictureDialog и SavePictureDialog являются частным случаем стандартных диалогов открыть-сохранить, но предназначенными для работы с графическими файлами. Визуально они отличаются от универсальных диалогов тем, что имеют область просмотра изображения. С точки зрения их использования в программе они ничем не отличаются от OpenDialog и SaveDialog.

Вообще, следует отметить, что базовым классом для всех диалогов выступает TCommonDialog, а для всех диалогов работы с файлами — класс TOpenDialog. Итак, рассмотрим общие свойства файловых диалогов на основе OpenDialog, для чего обратимся к таблице 14.1.

Таблица 14.1. Свойства файловых диалогов

Свойство Тип Описание
DefaultExt String Определяет расширение по умолчанию, которое будет добавляться к файлу, если пользователь не указал расширение явно
FileName String Определяет имя файла, включая его путь
Files TStrings Содержит список выбранных файлов, если таковых несколько (задается свойством Options путем включения флага ofAllowMultiSelect)
Filter String Определяет строку, содержащую фильтр типов файлов
FilterIndex Integer Определяет, какой из вариантов фильтра должен быть выбран по умолчанию
InitialDir String Определяет каталог, который будет отображен изначально
Options TOpenOptions Определяет вид особенности поведения диалога
OptionsEx TOpenOptionsEx Определяет дополнительные опции, актуальные для Windows Me, 2000 и более новых версий
Title String Определяет текст, который будет отображен в заголовке диалога

Наиболее востребованным свойством любого файлового диалога, разумеется, являются свойство FileName, поскольку именно оно определяет имя файла, выбранного пользователем. В то же время для настроек диалога важны и другие свойства, в частности, при помощи свойства Filter задают маску, по которой пользователь сможет фильтровать типы файлов. И хотя это свойство представляет собой строку, для его правки при посредстве инспектора объектов используется табличный редактор. Более того, для специализированных диалогов, предназначенных для работы с графическими файлами, это свойство заполняется автоматически (рис. 14.1).

Рис. 14.1. Редактор свойства Filter с данными по умолчанию для OpenPictureDialog

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

OpenDialog1.Filter:=’Все файлы|*.*|Текстовые файлы|*.txt’;

Здесь мы определили 2 варианта фильтра — для отображения всех файлов (шаблон *.*) и для отображения только текстовых файлов (шаблон *.txt). Если один шаблон включает в себя несколько разных расширений, то они перечисляются через точку с запятой:

OpenDialog1.Filter:=’Все файлы|*.*|Файлы Delphi|*.pas;*.dpr’;

Еще одно важное свойство файловых диалогов — это Options. Именно при помощи множества флагов этого свойства определяют особенности поведения диалога, а также устанавливают отображение дополнительных элементов его интерфейса. Значения всех актуальных флагов приведены в таблице 14.2.

Таблица 14.2. Значения флагов свойства Options компонента OpenDialog

Флаг Описание
ofReadOnly «Делает опцию «»только чтение»» включенной по умолчанию»
ofOverwritePrompt Указывает на необходимость вывода предупреждающего сообщения, если файл с указанным именем уже существует (для диалогов сохранения)
ofHideReadOnly «Удаляет из диалога переключатель «»только чтение»»»
ofNoChangeDir Возвращает исходный путь после закрытия диалога
ofShowHelp Отображает кнопку справки на диалоге
ofNoValidate Отключает проверку на недопустимые символы в имени файла
ofAllowMultiSelect Позволяет пользователю выбрать несколько файлов одновременно
ofExtensionDifferent Этот флаг включается автоматически, когда выбранный файл имеет расширение, отличное от указанного в свойстве DefaultExt
ofPathMustExist Выдает сообщение об ошибке, если указанного пользователем пути не существует
ofFileMustExist Выдает сообщение об ошибке, если указанного пользователем файла не существует (для диалогов открытия файла)
ofCreatePrompt Выдает сообщение с предупреждением о необходимости создания файла, если указанного пользователем файла не существует
ofShareAware Позволяет игнорировать ошибки доступа
ofNoReadOnlyReturn Выдает сообщение об ошибке, если пользователь выберет файл, доступный только для чтения
ofNoTestFileCreate Отключает проверку на права доступа к сетевым ресурсам
ofNoDereferenceLinks Отключает обработку ярлыков. Т.е. если этот флаг не установлен, то выбор ярлыка приведет к выбору файла, на который ярлык ссылается, в противном случае будет выбран сам файл ярлыка (lnk)
ofEnableSizing Позволяет пользователю изменять размеры диалогового окна (не действует в Windows 95 и Windows NT 4.0)
ofDontAddToRecent Не позволяет заносить выбранные файлы в список недавно открытых документов
ofForceShowHidden Позволяет пользователю увидеть скрытые файлы

По умолчанию включены только ofHideReadOnly и ofEnableSizing, т.е. скрыта редко используемая на практике опция открытия файлов в режиме «только чтение», и разрешено изменение размеров диалогового окна.

Что касается второго свойства — OptionsEx, то для него пока что доступна единственная настройка — ofExNoPlacesBar, позволяющая отключить боковую панель быстрого доступа к основным разделам компьютера, поддерживаемую в Windows Me, 2000, XP и 2003 Server.

Еще проще дела обстоят с методами. Кроме стандартных методов, доставшихся диалогам от класса TComponent, у него всего один собственный метод — Execute. Именно при помощи этого метода вызываются диалоги в коде программы. Метод Execute возвращает булевское значение — истину, если пользователь нажмет ОК, или ложь, если пользователь выберет отмену. Соответственно, типичный вызов диалога в программе выглядит следующим образом:

if OpenDialog1.Execute then begin . end;

В том же случае, если единственным предназначением процедуры или функции, обращающейся к диалогу, является получение имени файла (например, если это обработчик для кнопки типа «открыть файл»), то удобнее использовать отрицание:

procedure TForm1.OpenFileButtonClick(Sender: TObject); begin if not OpenDialog1.Execute then exit; Form1.Caption:=OpenDialog1.FileName; Memo1.Lines.LoadFromFile(OpenDialog1.FileName); end;

Такой подход позволяет избежать лишнего блока begin-end, поскольку если диалог не выполнен (т.е. пользователь передумал открывать файл), то можно сразу же выйти из подпрограммы, предназначенной для обработки загрузки файла.

Диалоги печати

Для взаимодействия со средствами Windows по подготовке к печати в VCL предусмотрено 3 компонента: PrintDialog, PrintSetupDialog и PageSetupDialog. Первый из них реализует доступ к стандартному диалогу печати, второй — к диалогу настройки печати, а третий — к диалогу параметров страницы.

Свойства, которыми обладают данные компоненты, в основном, предназначены для непосредственного обмена информацией между приложением и окном диалога, вернее, даже с его составными элементами. Так, для диалога печати (PrintDialog, рис. 14.2), это будут свойства, отвечающие за диапазон печати и число копий. В частности, за число копий отвечает свойство Copies, за тип диапазона, который может принимать одно из 3 значений — все (prAllPages), указанные страницы (prPageNums), или выделенный фрагмент (prSelection) — отвечает свойство PrintRange. При этом, если выбран диапазон по страницам, то его границы будут определяться свойствами FromPage и ToPage. Ограничить границы выбора этих значений пользователем во время его работы с диалогом можно при помощи свойств MaxPage и MinPage.

Рис. 14.2. Диалог печати

Как и у большинства других диалогов, диалог печати имеет свойство Options, при помощи которого можно настроить вид и поведение диалога. Приведем описание значений его флагов:

  • poDisablePrintToFile — Отключает (делает недоступной) опцию печати в файл, актуально только если включен флаг poPrintToFile;
  • poHelp — Отображает кнопку справки на окне диалога;
  • poPageNums — Делает доступной опцию печати диапазона страниц;
  • poPrintToFile — Отображает переключатель вывода в файл;
  • poSelection — Делает доступной опцию печати выделенного фрагмента;
  • poWarning — Производит проверку на доступность выбранного принтера, и выдает предупреждение, если устройство не доступно.

Важно осознавать, что сам по себе диалог печати ничего не делает. Фактически он лишь предоставляет пользовательский интерфейс для выбора диапазона печати и указания числа копий. Дальнейшую обработку полученной информации должно выполнять само приложение, для чего используется объект Printer (см. главу 9). В частности, объект «принтер» имеет свойство Copies, обозначающее количество копий, так что с этой точки зрения все просто, надо лишь не забыть включить модуль Printers в блок Uses, дальше останется лишь присвоить соответствующее свойство:

Ряд других свойств объекта Printer настраиваются при помощи диалога настроек печати, PrinterSetupDialog. Однако, в отличие от диалога печати, изменения, произведенные пользователем в диалоге настроек, а именно — размер бумаги, устройство подачи и ориентация страницы — будут применены автоматически. Во многом это связано с тем, что данные настройки напрямую зависят от аппаратного обеспечения: ведь вы не можете установить размер бумаги, не поддерживаемый принтером, или назначить в качестве подающего лотка устройство, которого нет физически.

Еще один диалог, имеющий отношение к печати — это диалог настройки параметров страницы, PageSetupDialog, появившийся в Delphi 7. С его помощью пользователь может указать ориентацию страницы (портретную или ландшафтную), установить размер листа и источник подачи, а также задать поля. Иначе говоря, этот диалог предоставляет больше возможностей для выбора параметров страницы, чем PrinterSetupDialog. Впрочем, при помощи свойства Options можно отключить те или иные настройки, как-то выбор размера листа, его ориентации, полей, или устройства подачи:

  • psoDefaultMinMargins — Учитывает минимальные значения, заданные в свойствах для полей страницы;
  • psoDisableMargins — Отключает элементы интерфейса, относящиеся к установке полей;
  • psoDisableOrientation — Отключает элементы интерфейса, относящиеся к выбору ориентации страницы;
  • psoDisablePagePainting — Отключает вывод демонстрационного рисунка;
  • psoDisablePaper — Отключает элементы интерфейса, относящиеся к размеру и подаче бумаги;
  • psoDisablePrinter — Делает недоступной кнопку выбора принтера;
  • psoMargins — Устанавливает начальные значения полей в значения, заданные при помощи свойств диалога. В противном случае использует значения по умолчанию;
  • psoMinMargins — Устанавливает минимальные значения полей в значения, заданные при помощи свойств диалога. В противном случае использует значения по умолчанию;
  • psoShowHelp — Делает доступной кнопку вызова справки;
  • psoWarning — Выводит предупреждение, если в системе не выбран принтер по умолчанию.

Что касается тех параметров, которые не назначаются принтеру автоматически (в частности, поля), то как раз они и представлены свойствами компонента PageSetupDialog. Это свойства MarginLeft, MarginTop, MarginRight и MarginBottom, отвечающие за поля слева, сверху, справа и снизу, соответственно. При этом существует еще и возможность ограничить минимальный размер полей, что делается путем включения флага psoDefaultMinMargins, а сами значения задаются при помощи свойств MinMarginLeft, MinMarginTop, MinMarginRight и MinMarginBottom. Типом данных для всех этих свойств является целое, однако вопрос состоит в том, что за единицы измерения они представляют. За этот вопрос отвечает свойство Units, которое может принимать 3 значения:

  • pmDefault — используются единицы измерения, принятые в системе по умолчанию;
  • pmInches — значения интерпретируются как сотые доли дюйма;
  • pmMillimeters — значения интерпретируются как сотые доли миллиметра.

Эти же единицы применяются не только к полям, но и еще к 2 свойствам — PageWidth и PageHeight, представляющими собой размеры страницы в ширину и в высоту. Так, если свойство Units установлено в pmMillimeters, а в качестве носителя пользователь выберет лист размером A4, эти свойства примут значения 21000 и 29700, т.е. 210 мм и 297 мм.

Подобно диалогам для работы с файлами, диалоги печати имеют в своем распоряжении единственный собственный метод — Execute, который следует использовать для вызова окна диалога по ходу выполнения программы.

Илон Маск рекомендует:  Что такое код mhash_count

Диалог шрифта

Диалоги открытия файлов и печати можно отнести к группе ввода-вывода данных. Пожалуй, лишь диалог параметров страницы относится к их форматированию. В то же время, существует еще диалоги, ориентированные именно на форматирование. В частности, это диалог параметров шрифта, представленный компонентом FontDialog.

Основным свойством этого диалога можно назвать свойство Font, при помощи которого данный диалог обменивается информацией о шрифте с программой. С типом TFont мы уже отчасти знакомы, в частности, с его свойствами Color, Name, Size, и Style. Так же имеются свойства Charset и Height, определяющие набор символов и высоту, соответственно. При этом значение высоты напрямую зависит от размера шрифта, задаваемого свойством Size. Вместе с тем, для диалога параметров шрифта важно именно свойство Size, поскольку в самом диалоге пользователь указывает именно это свойство. Что касается свойства Charset, то его возможные значения определяются выбранным шрифтом. Для шрифтов OpenType, основанных на Unicode и используемых в новейших версиях Windows, допустимо наличие множества наборов символов, для обычных же TrueType шрифтов наборов бывает не более 2, обычно это основной латинский (127 символов ANSI) и один из дополнительных — греческий, восточноевропейский, кириллический и т.д. Значение набора символов задается при помощи констант, определенных в модуле Graphics, или непосредственно числами. Так, для ANSI это будет 0, для символьных шрифтов — 2, а для кириллических — 204.

Подобно другим диалогам, диалог параметров шрифта имеет свойство Options. В данном случае оно имеет следующий набор флагов:

  • fdAnsiOnly — Делает доступными для выбора только шрифты, поддерживающие ANSI-символы. Символьные шрифты будут исключены;
  • fdApplyButton — Отображает кнопку «Применить», действие для которой можно задать при помощи обработчика события onApply;
  • fdEffects — Отображает группу видоизменения (эффекты зачеркивания, подчеркивания и цвет шрифта);
  • fdFixedPitchOnly — Делает доступными только моноширинные шрифты;
  • fdForceFontExist — Выполняет проверку на существование шрифта, введенного пользователем и выводит сообщение об ошибке, если такового нет;
  • fdLimitSize — Включает проверку на границы размеров шрифта, задаваемых при помощи свойств MaxFontSize и MinFontSize;
  • fdNoFaceSel — Диалог появляется без предварительно выбранного шрифта;
  • fdNoOEMFonts — Исключает OEM-шрифты из списка доступных шрифтов;
  • fdScalableOnly — Исключает не масштабируемые (битовые, или Type 1) шрифты из списка;
  • fdNoSimulations — Отображает только те варианты начертания, которые явно определены в файлах шрифта. Генерируемые наклонные и полужирные начертания не предлагаются;
  • fdNoSizeSel — Диалог появляется без предварительно выбранного размера;
  • fdNoStyleSel — Диалог появляется без предварительно выбранного стиля;
  • fdNoVectorFonts — Исключает векторные («плоттерные») шрифты;
  • fdShowHelp — Отображает кнопку справки на диалоге;
  • fdTrueTypeOnly — Делает доступными только шрифты типа TrueType;
  • fdWysiwyg — Отображает только шрифты, доступные для экрана и принтера (кроме TrueType).

Чаще всего диалог шрифта используют совместно с текстовым редактором. Допустим, если у нас имеется приложение, состоящее из многострочного редактора (компонент Memo) и предназначенное для просмотра текстовых файлов. Как минимум, такое приложение будет иметь 2 визуальных компонента — редактор и кнопку для вызова диалога открытия файла, а так же 1 невизуальный, представляющий собой собственно диалог открытья файла. Мы можем предоставить пользователю возможность изменять шрифт, которым отображаются файлы в редакторе, для чего нам потребуется компонент FontDialog и кнопка для обращения к этому диалогу. Не помешает добавить еще одну кнопку — для выхода из программы. Таким образом, мы получим приложение, вид которого в Delphi IDE будет примерно таким, как показано на рис. 14.3.

Рис. 14.3. Приложение для просмотра текстовых файлов

Из свойств, назначенных использованным компонентам, отметим лишь свойства Caption для всех 3 кнопок, а так же свойство Filter для диалога открытия файла. Пусть оно будет определено следующим образом:

В данном случае фильтр будет иметь 2 варианта: «текстовые» (для файлов с расширениями txt, bat, ini, pas и dpr), а так же «все», для файлов с любым расширением. После этого остается добавить обработчики события OnClick для всех 3 кнопок, в результате чего мы получим программу, код которой приведен в листинге 14.1.

Листинг 14.1. Исходный код программы просмотра файлов с выбором шрифта

unit Unit1; interface uses Windows, >

Как видно из процедуры, обрабатывающей нажатие кнопки «Шрифт», для того, чтобы изменить все параметры шрифта сразу, достаточно присвоить свойству Font компонента-редактора значение одноименного свойства диалога. Если бы нам требовалось присвоить лишь часть атрибутов (например, только название гарнитуры и размер кегля), то следовало бы присваивать соответствующие свойства типа TFont по отдельности:

Точно так же можно производить и обратную операцию, т.е. если нам надо добиться того, чтобы при вызове диалога все свойства были установлены в соответствие с теми, что имеются у редактора, то перед обращением к его методу Execute, было бы достаточно присвоить свойству Font диалога значение свойства Font редактора.

Диалог цвета

Диалог параметров шрифта позволяет установить любые его параметры, включая цвет. Но он не предназначен для того, чтобы изменять цвет заднего плана. Для этих целей следует использовать специальный диалог выбора цвета, в VCL он представлен компонентом ColorDialog. С точки зрения свойств он аналогичен диалогу параметров, с той лишь разницей, что вместо свойства Font, определяющем шрифт, у него имеется свойство Color, определяющее цвета.

Что касается типичного для диалогов свойства Options, то оно у ColorDialog имеет всего 5 флагов:

  • cdFullOpen — Отображает диалог в развернутом виде, с палитрой выбора произвольных цветов;
  • cdPreventFullOpen — Запрещает пользователю раскрывать диалог для выбора произвольных цветов;
  • cdShowHelp — Отображает кнопку справки на диалоге;
  • cdSolidColor — Указывает ОС на необходимость использования ближайшего системного цвета (из фиксированной палитры Windows) взамен выбранного пользователем:
  • cdAnyColor — Позволяет пользователю выбрать цвет, который может быть отображен только путем смешивания нескольких цветов (что актуально, если глубина цветопередачи меньше 24 бит).

По умолчанию предлагается палитра, состоящая всего лишь из 48 цветов, кроме того, еще 16 цветов могут быть заданы разработчиком при помощи свойства CustomColors. Это свойство имеет тип TStrings и должно состоять из строк типа имя=значение, где в качестве имени используется слово Color и буква, от a до p по алфавиту (итого 16 вариантов максимум), а в качестве значения — цвет, заданный при помощи RGB-триплета:

ColorA=00CCDD ColorB=DFCA72 . ColorP=234567

Если же пользователь раскроет диалог (если это не запрещено флагом cdPreventFullOpen), или же диалог изначально открывается в полном виде (флаг cdFullOpen), то либо визуально, используя палитру, либо указывая числовые значения в формате HSB или RGB, пользователь сможет выбрать любой цвет (рис. 14.4).

Рис. 14.4. Диалог выбора цвета в свернутом (слева) и в развернутом (справа) режиме

Чтобы проиллюстрировать работу этого диалога, добавим к приведенному в листинге 14.1 приложению компонент ColorDialog и еще одну кнопку, которую назовем «Фон». При этом код для события OnClick получится следующим:

procedure TForm1.Button4Click(Sender: TObject); begin ColorDialog1.Color:=Memo1.Color; if not ColorDialog1.Execute then exit; Memo1.Color:=ColorDialog1.Color; end;

Здесь вначале диалогу присваивается цвет фона редактора, после чего диалог вызывается, и, в случае положительного срабатывания, фону редактора назначается новый цвет. Модернизированное таким образом приложение можно посмотреть в каталоге Demo\Part3\Dlg2.

Редактор RTF

Рассмотренные выше диалоги для работы со шрифтами и с цветом представляют наибольшую ценность для форматирования текста. Для этих целей в Windows имеется специальный компонент — редактор форматированного текста, представленный в библиотеке VCL компонентом RichEdit, относящимся к компонентам Win32. Этот компонент фактически делает то же, что и все остальные компоненты этой группы — предоставляет простой и удобный доступ к одному из стандартных системных элементов управления, причем в данном случае таковой представлен отдельным системным файлом — richedit32.dll.

В иерархии классов VCL компонент RichEdit является наследником класса TCustomMemo, на основе которого построен «обычный» многострочный редактор. Вместе с тем, этот компонент обладает рядом свойств и методов, позволяющих, с одной стороны, форматировать текст, а с другой — обеспечивающих ряд сервисных функций, вроде поиска совпадений или вывода на печать. Всего в распоряжении редактора форматированного текста имеется свыше 100 свойств. Впрочем, непосредственно у класса TCustomRichEdit, на основе которого и создан рассматриваемый компонент, определено только 12, при этом ряд из них — HideSelection, Lines, SelLength, SelStart и SelText являются лишь переопределение одноименных свойств уже рассмотренных нами разновидностей редакторов. В итоге нам остается рассмотреть не так уж и много новых свойств этого компонента — см. таблицу 14.3.

Таблица 14.3. Собственные свойства редактора RTF

Свойство Тип Описание
DefAttributes TTextAttributes Определяет характеристики текста по умолчанию
DefaultConverter TConversionClass Определяет класс того объекта, который будет использоваться для преобразования формата текста. Автоматически используются преобразователи простого текста из и в RTF
HideScrollBars Boolean Определяет, должны ли полосы прокрутки появляться только при необходимости
PageRect TRect Определяет размеры страницы (в пикселях), которые будут использоваться для вывода на печать
Paragraph TParaAttributes Определяет параметры форматирования абзаца
PlainText Boolean Определяет тип текста – форматированный (false) или простой (true)
SelAttributes TTextAttributes Определяет характеристики текста выделенного фрагмента

Свойство DefAttributes типа TTextAttributes, задающее параметры шрифта, по своей сути похоже на свойство Font, имеющееся у многих других компонент, но доступно только во время выполнения. В то же время, в момент инициализации приложения, именно на основе параметров, указанных для свойства Font, и назначаются значения для свойства DefAttributes. Что касается типа TTextAttributes, то он имеет одно важное отличие от типа TFont, а именно — свойство ConsistentAttributes, которое показывает отличия выбранного фрагмента текста по отношению к остальному тексту. Впрочем, такая информация является более полезной для другого свойства редактора, а именно — для SelAttributes. Именно это свойство позволяет изменять параметры выделенной части текста, или же той его части, где находится каретка ввода. Поскольку текст может быть выделенным только в работающем приложении, то и это свойство доступно только во время выполнения.

Чтобы лучше представить себе суть свойств DefAttributes и SelAttributes, создадим небольшое приложение, которое выводило бы информацию о состоянии обоих этих свойств для компонента RichEdit. Для этого, помимо самого компонента RichEdit, нам понадобятся так же 2 компонента типа Memo — для вывода информации, а так же кнопка, по нажатию на которую интересующие нас сведения будут выводиться.

Кроме того, нам понадобится готовый файл в формате RTF, который будет содержать предварительно отформатированный различными способами текст. Подготовить его можно в любом текстовом процессоре, включая Word или WordPad, надо только будет при сохранении указать соответствующий формат файла. Для загрузки такого файла разместим на форме еще одну кнопку. В результате у нас получится форма с редактором RTF, 2 кнопками и 2 обычными редакторами (рис. 14.5).

Рис. 14.5. Приложение для тестирования DefAttributes и SelAttributes

Для обработчика события OnClick кнопки «Загрузить» достаточно будет написать следующий код:

Здесь подразумевается, что файл с примером текста называется simple.rtf и расположен в том же каталоге, что и исполняемый файл приложения.

Что касается кода, выводящего информацию о значениях исследуемых свойств, то, учитывая, что нам оба раза придется выводить сведения об объекте одного и того же типа — TtextAttribuitrs, то имеет смысл предварительно создать процедуру, которая могла бы получить нужные данные на обработку и вывести их в указанное место. Назовем ее PrintAttrInfo, и определим как публичную процедуру класса TForm1:

type TForm1 = class(TForm) . public procedure PrintAttrInfo(a: TTextAttributes; m: TMemo); end;

Реализация же этой процедуры будет заключаться в последовательном добавлении в указанный в качестве 2-го аргумента редактор строк со значениями атрибутов:

procedure TForm1.PrintAttrInfo(a: TTextAttributes; m: TMemo); var s: string; begin m.Lines.Clear; //предварительно очищаем содержимое m.Lines.Add(‘Charset: ‘+IntToStr(a.Charset)); //набор символов m.Lines.Add(‘Colour: ‘+IntToStr(a.Color)); //цвет m.Lines.Add(‘Name: ‘+a.Name); //гарнитура < для текстового вывода информации о типе шрифта определим, какой из 3 возможных вариантов используется >case a.Pitch of fpDefault: m.Lines.Add(‘Pitch: fpDefault’); fpVariable: m.Lines.Add(‘Pitch: fpVariable’); fpFixed: m.Lines.Add(‘Pitch: fpFixed’); end; m.Lines.Add(‘Size: ‘+IntToStr(a.Size)); //размер < поскольку шрифт может одновременно иметь сразу несколько признаков свойства Style, то проверим их все >if fsBold in a.Style then s:=’fsBold ‘; if fsItalic in a.Style then s:=s+’fsItalic ‘; if fsUnderline in a.Style then s:=s+’fsUnderline ‘; if fsStrikeOut in a.Style then s:=s+’fsStrikeOut ‘; m.Lines.Add(‘Style: [ ‘+s+’]’); end;

Наконец, для кнопки «Показать» остается написать 2 вызова определенной нами процедуры PrintAttrInfo:

PrintAttrInfo(RichEdit1.DefAttributes, Memo1); PrintAttrInfo(RichEdit1.SelAttributes, Memo2);

Если теперь запустить это приложение и нажать на кнопку «Показать», то можно будет увидеть, что для обоих свойств отображаются одни и те же значения. Если же загрузить файл с форматированным текстом, то значения свойства SelAttributes изменятся. Более того, если в загруженном файле применены различные шрифты или стили оформления, то, изменяя текущую позицию каретки, можно будет видеть текущие атрибуты шрифта. В то же время, если изменить значение свойства Font, то изменятся не только значение свойства DefAttributes, но и параметры всего текста. Чтобы в этом убедиться, можно добавить еще одну кнопку, которая будет вызывать компонент FontDialog и написать для нее следующий код:

FontDialog1.Font:=RichEdit1.Font; if FontDialog1.Execute then RichEdit1.Font:=FontDialog1.Font;

Таким образом, после обращения к диалогу шрифта и назначению новых данных свойству Font, изменится оформление всего текста. Готовый пример можно найти в каталоге Demo\Part3\Rich1.

Еще одно свойство, специфическое для редактора RTF — это Paragraph. Оно позволяет задать ряд параметров текста, относящихся к форматированию абзацев, включая выравнивание, стиль списка, размер отступов и табуляции. Оно имеет тип TParaAttributes, который, в свою очередь, содержит следующие свойства:

  • Alignment — определяет выравнивание, может принимать значения taLeftJustify, taRightJustify, taCenter для выравнивания по левому краю, по правому краю и по центру, соответственно;
  • FirstIndent — определяет размер «красной строки» в пикселях;
  • LeftIndent и RightIndent — определяют, соответственно, отступы от левого и правого полей в пикселях;
  • Numbering — отвечает за стиль «нумерации», должен ли параграф оформляется как пункт списка (nsBullet) или нет (nsNone).

При работе с редактором форматированного текста следует учитывать, что в разных версиях Windows поддерживаются разные версии редактора. Так, в Windows 95 это версия 1.0, в Windows 98 — 2.0, а в Windows 2000 и XP — 3.0. Вместе с тем, если в системе с Windows 95 установлен Office 97 или MSIE 4.0, то компонент будет обновлен до версии 2.0. Разумеется, новые версии имеют обратную совместимость с предыдущими, однако на практике программа, использующая этот компонент, и запущенная под Windows 95 будет вести себя не совсем так, как та же программа, работающая в Windows XP. В основном, это связано с ошибками, допущенными при разработке первой редакции этого элемента управления для Windows 95. И хотя вряд ли вы найдете сейчас компьютер, работающий под этой версией ОС, да еще и без установленного Office, требования к обратной совместимости заставляют Borland от версии к версии Delphi оставлять свой компонент RichEdit привязанным к версии 1.0 данного элемента Windows. Поэтому, если вам придется на практике разрабатывать приложение, построенное вокруг этого компонента (т.е. приложение, в котором текстовый редактор является одной из важнейших составляющих), то используете сторонние компоненты, ориентированные на более новые версии этого системного элемента, например, RichEdit98. В частности, помимо более предсказуемого поведения и отсутствия специфических «особенностей», новые версии имеют более широкую функциональность, например, возможность выравнивания текста по ширине, а так же куда большие возможности оформления. В то же время, если RichEdit нужен лишь как обычный блокнот с готовыми функциями поиска и печати, то функциональности стандартного компонента будет вполне достаточно.

Пример текстового редактора

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

Но прежде, чем браться за разработку приложения, хотелось бы остановиться на таком аспекте, как правила именования компонент. По умолчанию, как мы знаем, Delphi присваивает им имена, убирая первую букву (T) и добавляя порядковый номер (1,2,3). Но на практике это не очень удобно, поскольку в достаточно большой программе трудно будет понять, что такое Button22 или CheckBox17. По этой причине существуют некие общие правила наименования компонентов. Сводятся они к тому, что используется 2-3-4 буквенный префикс (или, по желанию — суффикс), идентифицирующий принадлежность объекта к классу, в сочетании со словом, отражающем суть данного объекта. Например, текстовое поле (класс TEdit), предназначенное для ввода имени пользователя, согласно таким правилам может называться, скажем, EdtUserName или UserNameEd, а кнопка (TButton) запуска чего-либо — BtnStart или StartBtn.

Итак, для текстового редактора нам понадобится создать новое приложение, после чего разместить на его форме ряд компонент, необходимых для его работы. Прежде всего, это главное меню (MainMenu) и, разумеется, сам RichEdit. Очевидно, что нам пригодятся и стандартные диалоги, включая диалоги для работы с файлами (OpenDialog и CloseDialog), диалоги печати (PrintDialog и PrintSetupDialog), а так же диалоги для работы с цветом и шрифтами (FontDialog и ColorDialog). Теперь для компонента RichEdit установим свойство Align в AlClient, чтобы область редактора заняла все свободное место на форме (рис. 14.6).

Рис. 14.6. Окно текстового редактора в начале разработки

Разместив нужные нам компоненты на форме, установим имена следующим образом:

  • Форма — MainFrm;
  • Главное меню — MainMenu;
  • Редактор — RichEd;
  • Диалоги — OpenDlg, SaveDlg, PrintDlg, PrintSetupDlg, FontDlg и ColorDlg;

Теперь можно заняться обустройством главного меню. Предусмотрим в нем 3 раздела: Файл, Правка и Формат. Для этого откроем редактор меню (двойным щелчком по компоненту MainMenu) и создадим эти 3 основных пункта главного меню. Они автоматически получат названия N1, N2 и N3, но изменять их мы не будем по той простой причине, что эти пункты — лишь заголовки для самих меню, и в программном коде обращаться к ним нам не придется.

Далее заполним меню «Файл», создав в нем пункты «Открыть…», «Сохранить…», «Печать…», «Принтер…» и «Выход». Назовем их OpenFileM, SaveFileM, PrintFileM, PrintSetupFileM и ExitFileM. Общие суффиксы FileM в будущем, будут нам подсказывать, что мы имеем дело с меню файловых операций. А многоточия после слова в подписи первых 4 пунктов меню будут подсказывать пользователю, что данные пункты выполняются не сами по себе, а вызывают диалоговое окно. Внешний вид конструктора меню на данном этапе будет таким, как показано на рис. 14.7. Само меню в программе будет выглядеть практически так же, что может навести нас на мысль о необходимости добавить разделители перед пунктами «Печать» и «Выход». Для этого следует выбрать в конструкторе нужный пункт и нажать клавишу Ins на клавиатуре, после чего в появившемся новом пункте в качестве значения свойства Caption указать символ «-«.

Следующим пунктом у нас идет меню «Правка». Разместим в нем стандартные пункты «Отменить», «Вырезать», «Копировать», «Вставить» и «Выделить все», присвоив им имена UndoEdM, CutEdM, CopyEdM, InsertEdM и SelAllEdM, соответственно. При этом после отмены не помешает поместить разделитель. Наконец, в последнюю группу — «Формат» — внесем пункты «Шрифт…» и «Цвет…», назвав их FontFmtM и ColorFmtM.

Илон Маск рекомендует:  Идентификаторы в CSS

Теперь подготовим к работе файловые диалоги. Поскольку мы имеем дело с текстовым редактором типа RTF, то основным типом файла будет как раз RTF. Тем не менее, не помешает предусмотреть возможность открытия файлов другого типа, в частности обычных текстовых (TXT). Таким образом, для свойства Filter в инспекторе объекта следует указать следующее значение:

Файлы RTF|*.rtf|Текстовые файлы ASCII|*.txt|Все файлы|*.*

Что касается свойства DefaultExt, то для диалога сохранения было бы целесообразным указать rtf.

Для начала подготовительной работы уже сделано достаточно, впору приступать к разработке собственно рабочих функций программы. В частности, для пункта «Открыть» из меню «Файл» для события OnClick достаточно написать следующий код:

if OpenDlg.Execute then RichEd.Lines.LoadFromFile(OpenDlg.FileName);

Для сохранения было бы целесообразным попытаться заранее назначить файлу имя, соответствующее имени файла в диалоге открытия:

if if OpenDlg.FileName<>» then SaveDlg.FileName:=OpenDlg.FileName; if SaveDlg.Execute then RichEd.Lines.SaveToFile(SaveDlg.FileName);

Подобным образом выполняются и обращения к остальным диалогам в программе. Что касается элементов меню «Правка», то там так же не должно быть проблем: достаточно обращаться к соответствующим методам самого редактора для выполнения соответствующих операций. При этом, правда, было бы полезным предварительно проверять операцию на допустимость. Например, перед выполнением отмены (метод Undo) не помешает проверить состояние свойства-флага CanUndo:

if RichEd.CanUndo then RichEd.Undo;

То же самое относится и к операциям работы с буфером обмена — не к чему пытаться копировать в буфер выделенный текст, если такового нет:

if RichEd.SelText<>» then RichEd.CutToClipboard;

Пояснить следует, разве что, операцию вставки из буфера обмена: здесь было бы полезным предварительно убедиться, что в нем действительно находится текст. Для этого в список используемых модулей (uses) следует добавить модуль Clipbrd, сама же процедура проверки будет выглядеть следующим образом:

if Clipboard.HasFormat(CF_TEXT) then .

Здесь мы обратились к глобальному объекту Clipboard (он создается автоматически, подобно Screen или Application) и воспользовались его методом HasFormat, чтобы убедиться, что информация, находящаяся в буфере обмена, является текстом.

После создания обработчиков событий для всех пунктов меню, остается сохранить проект, выбрав в качестве имени файла формы «main», а файла проекта — «myedit». Таким образом, полный исходный код модуля main получится примерно таким, как показано в листинге 14.2.

Листинг 14.2. Исходный код редактора MyEdit

unit main; interface uses Windows, Messages, SysUtils, Variants, >» then SaveDlg.FileName:=OpenDlg.FileName; if SaveDlg.Execute then RichEd.Lines.SaveToFile(SaveDlg.FileName); end; procedure TMainFrm.PrintMClick(Sender: TObject); begin if PrintDlg.Execute then RichEd.Print(»); end; procedure TMainFrm.PrintSetupMClick(Sender: TObject); begin PrintSetupDlg.Execute; end; procedure TMainFrm.ExitFileMClick(Sender: TObject); begin close; end; procedure TMainFrm.FontFmtMClick(Sender: TObject); begin FontDlg.Font.Name:=RichEd.SelAttributes.Name; FontDlg.Font.Color:=RichEd.SelAttributes.Color; FontDlg.Font.Charset:=RichEd.SelAttributes.Charset; FontDlg.Font.Size:=RichEd.SelAttributes.Size; FontDlg.Font.Style:=RichEd.SelAttributes.Style; if not FontDlg.Execute then exit; RichEd.SelAttributes.Name:=FontDlg.Font.Name; RichEd.SelAttributes.Color:=FontDlg.Font.Color; RichEd.SelAttributes.Charset:=FontDlg.Font.Charset; RichEd.SelAttributes.Size:=FontDlg.Font.Size; RichEd.SelAttributes.Style:=FontDlg.Font.Style; end; procedure TMainFrm.ColorFmtMClick(Sender: TObject); begin ColorDlg.Color:=RichEd.SelAttributes.Color; if not ColorDlg.Execute then exit; RichEd.SelAttributes.Color:=ColorDlg.Color; end; procedure TMainFrm.UndoEdMClick(Sender: TObject); begin if RichEd.CanUndo then RichEd.Undo; end; procedure TMainFrm.CutEdMClick(Sender: TObject); begin if RichEd.SelText<>» then RichEd.CutToClipboard; end; procedure TMainFrm.CopyEdMClick(Sender: TObject); begin if RichEd.SelText<>» then RichEd.CopyToClipboard; end; procedure TMainFrm.InsertEdMClick(Sender: TObject); begin if Clipboard.HasFormat(CF_TEXT) then RichEd.PasteFromClipboard; end; procedure TMainFrm.SelAllEdMClick(Sender: TObject); begin RichEd.SelectAll; end; end.

Если теперь запустить программу (готовый исходный код находится в каталоге Demo\Part3\Editor), то можно будет убедиться, что она действительно работает — открывает файлы, позволяет вводить и редактировать текст, изменяя его оформление. Из недостатков можно сходу выделить лишь надпись «RichEd», которую редактор содержит изначально и отсутствие полос прокрутки, если текста будет больше, чем помещается в окне. Первый недостаток исправляется правкой свойства Lines, путем удаления ненужного текста, а второй — путем назначения свойству ScrollBars значения ssBoth. Отметим, что если при этом свойство HideScrollBars оставить в значении истины, то полосы прокрутки будут появляться лишь при необходимости.

Более серьезный недостаток кроется в заложенной нами возможности работать не только с файлами RTF, но и с обычными текстовыми. Дело в том, что если при сохранении вы даже выберите тип текстовых файлов и укажете расширение txt, то программа все равно сохранит файл с разметкой. Но, как нам известно, у компонента RichEdit существует свойство PlainText, отвечающее за текущий формат. Таким образом, остается лишь установить нужное значение для этого свойства в момент перед сохранением файла. А для того, чтобы определить, какой формат выбрал пользователь, используем свойство FilterIndex диалога сохранения:

if SaveDlg.Execute then begin RichEd.PlainText:=(SaveDlg.FilterIndex>1); RichEd.Lines.SaveToFile(SaveDlg.FileName); RichEd.PlainText:=false; end;

Отметим, что после сохранения выполняется принудительная установка формата RTF. Это необходимо с той точки зрения, что следующим шагом пользователя может быть попытка открытия файла RTF, который не сможет быть обработан, если включен режим PlainText.

Диалоги поиска и замены

Работа с текстом не ограничивается изменением параметров шрифта. Часто гораздо более полезными операциями являются такие, как поиск подстроки с возможной заменой ее на иной текст. Для этих целей так же предусмотрены стандартные диалоговые окна — FindDialog и ReplaceDialog. Свойства этих компонентов включают в себя стандартное для диалогов Options, а так же группу свойств, определяющих положение диалога на экране — Left, Top и Position. Но наиболее важным, безусловно, является свойство FindText, которое собственно и содержит строку для поиска. У диалога замены предусмотрено еще одно свойство — ReplaceText, которое определяет строку для замены.

Но рассмотрим для начала свойство Options. Применительно к диалогам поиска и замены оно имеет следующий набор флагов:

  • frDisableMatchCase — Делает недоступной опцию «С учетом регистра»;
  • frDisableUpDown — Делает недоступной опцию выбора направления поиска;
  • frDisableWholeWord — Делает недоступной опцию «Только слово целиком»;
  • frDown — Делает включенной опцию направления поиска «Вниз» (если этот флаг выключен, то будет выбрано направление «Вверх»);
  • frFindNext — Этот флаг включается автоматически, когда пользователь щелкает по кнопке «Найти далее»;
  • frHideMatchCase — Удаляет опцию «С учетом регистра»;
  • frHideWholeWord — Удаляет опцию «Только слово целиком»;
  • frHideUpDown — Удаляет опцию выбора направления поиска;
  • frMatchCase — Указывает, что выбрана опция «С учетом регистра»;
  • frReplace — Указывает, что должна быть произведена замена данного найденного вхождения (только для диалога замены);
  • frReplaceAll — Указывает, что должна быть произведена замена всех найденных вхождений (только для диалога замены);
  • frShowHelp — Отображает кнопку справки на диалоге;
  • frWholeWord — Указывает, что была выбрана опция «Только слово целиком».

По умолчанию включен только флаг frDown, что делает выбранным направление поиска к концу документа.

Использование данных диалогов подразумевает применение самостоятельно разработанных функций поиска и замены. В то же время, у такого компонента, как редактор RTF, имеется метод FindText, который существенно упрощает задачу. Он определен следующим образом:

function FindText(const SearchStr: string; StartPos, Length: Integer; Options: TSearchTypes): Integer;

Здесь в качестве SearchStr указывается строка для поиска, StartPos обозначает место, с которого следует начинать поиск, а Length — место, до которого следует производить поиск. В качестве Options можно указать флаги stWholeWord и stMatchCase, включающие распознавание слов целиком и регистра. Таким образом, мы можем модернизировать наш текстовый редактор таким образом, чтобы он поддерживал поиск текста (модернизированный вариант находится в каталоге Demo\Part3\Editor2).

Прежде всего, поместим на форму оба рассматриваемых компонента — FindDialog и ReplaceDialog, присвоив им имена FindDlg и ReplaceDlg. Затем откроем конструктор меню и в раздел «Правка» добавим разделитель и 2 новых пункта — «Найти…» и «Заменить. «, назвав их, соответственно, SearchEdM и ReplaceEdM. Теперь для пункта «Найти» определим процедуру вызова диалога поиска:

procedure TMainFrm.SearchEdMClick(Sender: TObject); begin FindDlg.Execute; end; Таким способом мы лишь отображаем диалог. Саму процедуру поиска следует разместить в обработчике события OnFind самого диалога. В простейшем случае она должна лишь определять место начала и конца поиска, а в случае нахождения искомого — выделять найденный фрагмент. Таким образом, мы можем получить примерно следующую процедуру: procedure TMainFrm.FindDlgFind(Sender: TObject); var StartPos, ToPos, FoundPos: Integer; begin StartPos:=RichEd.SelStart+RichEd.SelLength; ToPos:=Length(RichEd.Text)-StartPos; FoundPos:=RichEd.FindText(FindDlg.FindText, StartPos, ToPos, []); if FoundPos<>-1 then begin RichEd.SelStart:=FoundPos; RichEd.SelLength:=Length(FindDlg.FindText); RichEd.SetFocus; end else ShowMessage(‘Текст не найден!’); end;

Здесь вначале вычисляется точка начала поиска — StartPos, затем определяется длина поиска — ToPos, в типичном случае она должна равняться размеру текста от точки начала поиска до конца документа, после чего вызывается собственно метод FindText, а результат его работы присваивается переменной FoundPos. Затем, если результат не равен -1 (т.е. если вхождение подстроки найдено), фрагмент текста выделяется, и фокус ввода передается окну редактора, в противном случае выводится сообщение «Текст не найден».

Недостатком процедуры в таком виде является то, что она не учитывает возможных изменений, сделанных пользователем в окне поиска. В частности, не определяется ни варианты учета регистра символов, ни вариант поиска по словам. Для того чтобы задействовать эти опции, нам необходимо определить переменную типа TSearchTypes, которую надо будет установить в то или иное значение, в зависимости от состояния флагов диалога поиска. Инициализацию этой переменной (назовем ее Opt) следует проводить перед обращением к методу FindText, так что в начало процедуры добавим следующие строки кода:

if frMatchCase in FindDlg.Options then Opt:=[stMatchCase]; if frWholeWord in FindDlg.Options then Opt:=Opt+[stWholeWord];

Теперь наша функция поиска распознает заданные пользователем установки, так что можно перейти к реализации процедуры замены. Собственно вызов диалога замены производится точно таким же способом — путем обращения к методу Execute. При этом процедура поиска, реализуемая диалогом замены, будет в точности такой же, как и у диалога поиска — достаточно будет изменить имя компонента (см. так же листинг 14.3).

Что касается процедуры обработки события OnReplace, которое возникает, когда пользователь нажимает на кнопку «Заменить» или «Заменить все», то она, в простейшем случае будет выглядеть аналогичным образом, с той лишь разницей, что к ней добавляется замена вхождения на указанный пользователем текст. А именно после выделения найденного текста следует добавить еще одну строку кода:

Таким образом, одиночную замену можно считать реализованной. Однако если пользователь нажмет кнопку «Заменить все», то произойдет лишь одна замена. Следовательно, необходимо использовать цикл с постусловием. При этом цикл должен прерываться после первой же итерации, если пользователь нажал кнопку «Заменить», а не «Заменить все», либо в том случае, если искомой строки не найдено. Таким образом, мы получаем следующее условие:

repeat . until (FoundPos=-1) or not(frReplaceAll in ReplaceDlg.Options);

Если теперь заключить весь блок операций процедуры в этот цикл, за исключением, разве что, первых 2 строк, которые определяют параметры поиска, запустить программу и попытаться произвести замену по всем вхождениям, то можно будет убедиться, что все замечательно работает. Единственная проблема состоит в том, что по окончании работы, вне зависимости от того, были ли произведены замены или нет, вы получите сообщение «Текст не найден». Это объясняется тем, что при последней итерации в любом случае будет выполнен блок else условного оператора, проверяющего наличие вхождения искомого текста. Таким образом, необходимо вынести эту проверку за пределы цикла, а для того, чтобы определить, было ли что-либо найдено, используем переменную-счетчик, которую перед началом выполнения цикла установим в 0, а при каждой выполненной замене будем увеличивать на 1. Таким образом, мы сможем не только выдавать сообщение о том, что текст не найден, но и показывать количество произведенных замен, если пользователь нажимал на кнопку «Заменить все». Итоговый вариант процедуры замены приведен в листинге 14.3.

Листинг 14.3. Процедуры поиска и замены для редактора RTF и ReplaceDialog

procedure TMainFrm.ReplaceDlgFind(Sender: TObject); var StartPos, ToPos, FoundPos: Integer; Opt: TSearchTypes; begin if frMatchCase in ReplaceDlg.Options then Opt:=[stMatchCase]; if frWholeWord in ReplaceDlg.Options then Opt:=Opt+[stWholeWord]; StartPos:=RichEd.SelStart+RichEd.SelLength; ToPos:=Length(RichEd.Text)-StartPos; FoundPos:=RichEd.FindText(ReplaceDlg.FindText, StartPos, ToPos, Opt); if FoundPos<>-1 then begin RichEd.SelStart:=FoundPos; RichEd.SelLength:=Length(ReplaceDlg.FindText); RichEd.SetFocus; end else ShowMessage(‘Текст не найден!’); end; procedure TMainFrm.ReplaceDlgReplace(Sender: TObject); var i, StartPos, ToPos, FoundPos: Integer; Opt: TSearchTypes; begin if frMatchCase in ReplaceDlg.Options then Opt:=[stMatchCase]; if frWholeWord in ReplaceDlg.Options then Opt:=Opt+[stWholeWord]; i:=0; repeat StartPos:=RichEd.SelStart+RichEd.SelLength; ToPos:=Length(RichEd.Text)-StartPos; FoundPos:=RichEd.FindText(ReplaceDlg.FindText, StartPos, ToPos, Opt); if FoundPos<>-1 then begin RichEd.SelStart:=FoundPos; RichEd.SelLength:=Length(ReplaceDlg.FindText); RichEd.SelText:=ReplaceDlg.ReplaceText; RichEd.SetFocus; inc(i); end; until (FoundPos=-1) or not(frReplaceAll in ReplaceDlg.Options); if i=0 then ShowMessage(‘Текст не найден!’) else if frReplaceAll in ReplaceDlg.Options then ShowMessage(‘Произведено ‘+IntToStr(i)+’ замен’); end;

Таким образом, мы создали редактор, который, в принципе, может делать все необходимые в повседневной работе вещи. Для удобства остается только определить сочетания горячих клавиш для основных действий, прежде всего — для открытия и сохранения файлов, а так же для поиска и замены (горячие клавиши для редактирования текста типа Ctrl+Z или Ctrl+C поддерживаются автоматически). Чтобы назначить сочетания, в конструкторе меню для меню «Открыть» в свойстве ShortCut выберем Ctrl+O, для «Сохранить» — Ctrl+S, для «Найти» — Ctrl+F, а для «Заменить» — Ctrl+H. Все эти сочетания являются стандартными для данных действий в Windows, а изобретать собственные варианты для стандартных действий крайне не рекомендуется.

Полный список

— создаем AlertDialog
— настраиваем заголовок, сообщение, картинку и кнопки

Начнем знакомство с AlertDialog. Этот диалог используется, если вы хотите сообщить о чем-то пользователю или попросить его сделать выбор типа Да/Нет/Отмена.

Напишем приложение, которое при закрытии будет вызывать диалог о сохранении данных, аналогичный диалогу из программ MS Office . Если мы ответим Да, то данные сохранятся, если Нет – то не сохранятся, если Отмена – приложение не закроется.

Project name: P0601_AlertDialogSimple
Build Target: Android 2.3.3
Application name: AlertDialogSimple
Package name: ru.startandroid.develop.p0601alertdialogsimple
Create Activity: MainActivity

Добавим в res/values/strings.xml строки c текстами:

В обработчике кнопки onclick вызываем диалог.

В onCreateDialog мы создаем диалог. Для этого используется класс AlertDialog.Builder. Мы указываем заголовок, текст сообщения, иконку и кнопки. Диалог может содержать максимум три кнопки ответа: положительная, отрицательная и нейтральная. Для каждой указываем текст и обработчик. Метод create создает диалог и мы его возвращаем (return).

Обработчик кнопок myClickListener реализует интерфейс DialogInterface.OnClickListener и в нашем случае является общим для всех кнопок. В нем мы проверяем, какая кнопка была нажата:
если положительная (BUTTON_POSITIVE), то сохраняем данные и закрываем приложение
если отрицательная (BUTTON_NEGATIVE), то закрываем приложение без сохранения
если нейтральная (BUTTON_NEUTRAL), то не делаем ничего

В своем методе saveData выводим текст, что данные как-будто сохранены. Просто, чтобы убедиться, что метод выполняется.

Все сохраним и запустим приложение. Нажмем кнопку Выход:

то приложение закроется и метод saveData будет выполнен.

Если жмем Отмена, то диалог закроется и с приложением ничего не произойдет.
А если жмем Нет, то приложение закроется без вызова нашего метода saveData.

Вот так несложно и недолго создать диалог для взаимодействия с пользователем. Использованные нами атрибуты диалога не являются обязательными. Вы можете, например, не указывать заголовок, или сделать только одну кнопку, а не три.

Для указания заголовка, сообщения и текстов кнопок необязательно использовать переменные R.string. Есть аналогичные реализации методов, которые принимают на вход просто текст.

И еще пара советов.

1) Чтобы диалог вызывался не только по кнопке выход, но и при нажатии на кнопку Назад в приложении, добавьте вызов диалога в реализацию метода onBackPressed

2) А если хотите, чтобы вызванный диалог не закрывался по нажатию кнопки Назад, то используйте метод setCancelable:

На следующем уроке:

— используем метод подготовки диалога

Присоединяйтесь к нам в Telegram:

— в канале StartAndroid публикуются ссылки на новые статьи с сайта startandroid.ru и интересные материалы с хабра, medium.com и т.п.

— в чатах решаем возникающие вопросы и проблемы по различным темам: Android, Kotlin, RxJava, Dagger, Тестирование

— ну и если просто хочется поговорить с коллегами по разработке, то есть чат Флудильня

— новый чат Performance для обсуждения проблем производительности и для ваших пожеланий по содержанию курса по этой теме

OpenDialog

Компонент delphi OpenDialog не визуальный компонент предназначенный для поддержки операции открытия файлов способный работать с любыми типами файлов. При обращении к этому компоненту вызывается стандартное диалоговое окно открытия файла.

Типы искомых файлов задаются в свойстве Filter. В процессе проектирования это свойство проще всего задать с помощью редактора фильтров, который вызывается при нажатии кнопки с многоточием возле этого свойства в инспекторе объектов. При этом открывается окно редактора вид, которого представлен на рисунке 1. В его левой панели Filter Name вы записываете тот текст, который увидит пользователь в выпадающем списке «тип файла» диалога. А в правой панели Filter записывается разделенные точками с запятой шаблоны фильтра.

Для записи этого программным путем просто вписываем в нашу программу код представленный ниже.

Если понадобится программным образом описать несколько строк фильтра, то отделяйте строку от строки все тем же разделителем – вертикальной чертой(|)

Свойство FilterIndex позволяет использовать по умолчанию один из фильтров, если в вашей программе их несколько.

Для определения имени папки, открываемой в момент первого запуска диалога, воспользуйтесь свойством InitialDir при повторном открытии выбор каталога зависит от флага ofNoChangeDir в свойстве Options диалогового окна. Все флаги этого свойства можно увидеть в таблице ниже.

ofReadOnly Открывает окно в режиме «только для чтения»
ofHideReadOnly Скрывает флажок ReadOnly
ofOverwritePrompt Играет роль в диалогах записи файлов, запрашивает разрешение на перезапись при совпадении имени.
ofNoChangeDir Если флаг установлен, то при повторных запусках диалога он открывает папку определенную в свойстве InitialDir. Иначе будет открыт каталог с которым велась работа в последний раз.
ofShowHelp Дополняет диалог кнопкой помощи.
ofNoValidate Отключает контроль ввода недопустимого символа в имя файла
OfAllowMultiSelect Допускает одновременный выбор нескольких файлов
ofExtensionDifferent Наличие флага обычно контролируется программистом во время выполнения приложения. Он автоматически устанавливается приложениям в случае, когда расширение выбранного файла отличается от заданного в свойстве DefaultExt
ofPathMustExist Вызывает сообщение об ошибке, если пользователь указал неверный путь к файлу.
ofFileMustExist Вызывает сообщение об ошибке, если пользователь указал имя не существующего файла
ofCreatePrompt Запросит подтверждения на создание несуществующего файла.
ofShareAwere Разрешает совместный доступ к одному и тому же файлу
ofNoReadOnlyReturn Инициализирует сообщение об ошибке при попытке обратиться к файлу с атрибутом «только для чтения».
ofNoTestFileCreate Опция применяется при работе с разделяемым сетевым ресурсом в котором пользователь обладает правом создания новых файлов, но не может модифицировать существующие.
ofNoNetworkButton Скрывает кнопку доступа к сетевым ресурсам.
ofNoLongNames Показывает файлы с форматом имени.
ofOldStyleDialog Флаг обратной совместимости, создает диалоговое окно старого стиля.
ofNoDereferenceLinks Определяет порядок взаимодействия с ярлыками файлов.
ofEnableIncludeNotify Работает с Windows2000 и более поздними версиями.
ofEnableSizing Разрешает изменять размеры диалогового окна

Небольшой пример для вызова диалога открытия

Добавляем компонент в стандартный Message Dialog.

0 посетителей читают тему

0 пользователей, 0 гостей, 0 анонимных

Ответственность

Сайт не предоставляет электронные версии произведений, а занимается лишь коллекционированием и каталогизацией ссылок, присылаемых и публикуемых на форуме нашими читателями. Если вы являетесь правообладателем какого-либо представленного материала и не желаете чтобы ссылка на него находилась в нашем каталоге, свяжитесь с нами и мы удалим её. Файлы для обмена предоставлены пользователями сайта, и администрация не несёт ответственности за их содержание. Просьба не заливать файлы, защищенные авторскими правами, а также файлы нелегального содержания!

Понравилась статья? Поделиться с друзьями:
Кодинг, CSS и SQL